diff --git a/org.hl7.fhir.convertors/pom.xml b/org.hl7.fhir.convertors/pom.xml
index 2231ed1dc..5d553f3a8 100644
--- a/org.hl7.fhir.convertors/pom.xml
+++ b/org.hl7.fhir.convertors/pom.xml
@@ -111,6 +111,14 @@
junit-jupiter-params
test
+
+
+ org.xerial
+ sqlite-jdbc
+ 3.42.0.0
+
+
+
diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/resources40_50/ValueSet40_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/resources40_50/ValueSet40_50.java
index a1dc9f452..533072ccc 100644
--- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/resources40_50/ValueSet40_50.java
+++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/resources40_50/ValueSet40_50.java
@@ -465,7 +465,11 @@ public class ValueSet40_50 {
tgt.addDesignation(convertConceptReferenceDesignationComponent(t));
for (org.hl7.fhir.r4.model.Extension t : src.getExtension()) {
if ("http://hl7.org/fhir/5.0/StructureDefinition/extension-ValueSet.expansion.contains.property".equals(t.getUrl())) {
- tgt.addProperty().setCode(t.getExtensionString("code")).setValue(ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().convertType(t.getExtensionByUrl("value").getValue()));
+ ConceptPropertyComponent prop = tgt.addProperty();
+ ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(t, prop, "code", "value");
+ prop.setCode(t.getExtensionString("code"));
+ prop.setValue(ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().convertType(t.getExtensionByUrl("value").getValue()));
+
}
}
for (org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent t : src.getContains())
@@ -494,6 +498,7 @@ public class ValueSet40_50 {
tgt.addDesignation(convertConceptReferenceDesignationComponent(t));
for (org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent t : src.getProperty()) {
org.hl7.fhir.r4.model.Extension ext = tgt.addExtension().setUrl("http://hl7.org/fhir/5.0/StructureDefinition/extension-ValueSet.expansion.contains.property");
+ ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(t, ext, "code", "value");
ext.addExtension().setUrl("code").setValue(ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().convertType(t.getCodeElement()));
ext.addExtension().setUrl("value").setValue(ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().convertType(t.getValue()));
}
diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ICFImporter.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ICFImporter.java
new file mode 100644
index 000000000..c5b21306e
--- /dev/null
+++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ICFImporter.java
@@ -0,0 +1,96 @@
+package org.hl7.fhir.convertors.misc;
+
+
+import java.util.Map;
+import java.util.HashMap;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.IOException;
+
+import org.fhir.ucum.Utilities;
+import org.hl7.fhir.exceptions.FHIRException;
+import org.hl7.fhir.r5.model.BooleanType;
+import org.hl7.fhir.r5.model.CodeType;
+import org.hl7.fhir.r5.model.CodeSystem;
+import org.hl7.fhir.r5.model.CodeSystem.CodeSystemHierarchyMeaning;
+import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
+import org.hl7.fhir.r5.model.CodeSystem.PropertyType;
+import org.hl7.fhir.r5.model.StringType;
+import org.hl7.fhir.r5.model.UriType;
+import org.hl7.fhir.r5.formats.IParser.OutputStyle;
+import org.hl7.fhir.r5.formats.JsonParser;
+import org.hl7.fhir.utilities.CSVReader;
+
+public class ICFImporter {
+
+ public static void main(String[] args) throws FHIRException, FileNotFoundException, IOException {
+ new ICFImporter().doImport(args[0], args[1]);
+
+ }
+
+ private void doImport(String src, String dst) throws FHIRException, FileNotFoundException, IOException {
+ CSVReader csv = new CSVReader(new FileInputStream(src));
+ csv.setDelimiter('\t');
+ csv.readHeaders();
+
+ CodeSystem cs = new CodeSystem();
+ cs.setId("icf");
+ cs.setUrl("http://id.who.int/icd/release/11/beta/icf");
+ cs.setVersion("2023-06");
+ cs.setName("WHOICF");
+ cs.setTitle("WHO ICF");
+ cs.setHierarchyMeaning(CodeSystemHierarchyMeaning.CLASSIFIEDWITH);
+ cs.setCopyright("© World Health Organization 2022\r\nSome rights reserved. This work is available under the Creative Commons Attribution-NoDerivatives 3.0 IGO license (CC BY-ND 3.0 IGO further specified at [[https://icd.who.int/en/docs/ICD11-license.pdf]]). \r\nUnder the terms of this license, you may copy and redistribute the work, provided the work is appropriately cited, as indicated below. In any use of this work, there should be no suggestion that WHO endorses any specific organization, products or services. The use of the WHO logo is not permitted. This license does not allow you to produce adaptations of the work (including translations) without permission from WHO.\r\nAny mediation relating to disputes arising under the license shall be conducted in accordance with the mediation rules of the World Intellectual Property Organization.\r\nThis FHIR version of ICD-11 was generated to support the FHIR Community. The definitive version of ICD-11 is available from [[https://icd.who.int/browse11/l-m/en]].\r\n");
+
+ cs.addProperty().setCode("icd11-uri").setDescription("Entity URI to map to ICD_11").setType(PropertyType.CODE);
+ cs.addProperty().setCode("IsResidual").setDescription("True if the concept is not completely defined by ICD-11").setType(PropertyType.BOOLEAN);
+ Map codes = new HashMap<>();
+
+ while (csv.line()) {
+ String code = csv.cell("Code");
+ if (!Utilities.noString(code)) {
+ ConceptDefinitionComponent c = new ConceptDefinitionComponent();
+ c.setCode(code);
+ c.setDisplay(fixDisplay(csv.cell("Title")));
+ c.addProperty().setCode("uri").setValue(new CodeType(csv.cell("Linearization (release) URI")));
+ String b = csv.cell("IsResidual").toLowerCase();
+ if (!"false".equals(b)) {
+ c.addProperty().setCode("IsResidual").setValue(new BooleanType(b));
+ }
+ Integer level = Integer.parseInt(csv.cell("DepthInKind"));
+ if (level == 1) {
+ cs.getConcept().add(c);
+ } else {
+ ConceptDefinitionComponent p = codes.get(level -1);
+ p.getConcept().add(c);
+ }
+ codes.put(level, c);
+ for (int i = level + 1; i < 100; i++) {
+ if (codes.containsKey(i)) {
+ codes.remove(i);
+ }
+ }
+ }
+
+ }
+ csv.close();
+ new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(new FileOutputStream(dst), cs);
+ }
+//
+// private String processLink(String cell) {
+// String[] p = cell.split("\\\"\\\"");
+// return p[1];
+// }
+
+ private String fixDisplay(String cell) {
+ int i = 0;
+ while (i < cell.length() && (cell.charAt(i) == ' ' || cell.charAt(i) == '-')) {
+ i++;
+ }
+ return cell.substring(i);
+ }
+
+}
diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/OMOPImporter.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/OMOPImporter.java
index 381b66b0d..c9d5ed707 100644
--- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/OMOPImporter.java
+++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/OMOPImporter.java
@@ -1,15 +1,18 @@
package org.hl7.fhir.convertors.misc;
import java.sql.DriverManager;
+import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.Map;
import java.util.HashMap;
+import org.hl7.fhir.convertors.misc.OMOPImporter.Tracker;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.utilities.CSVReader;
import org.hl7.fhir.utilities.Utilities;
+import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
@@ -17,101 +20,341 @@ import java.sql.Connection;
public class OMOPImporter {
+ public class Tracker {
+ private int blip;
+ private long start;
+ private int counter = 0;
+ boolean processed = false;
+
+ public Tracker(String name, int estimate) {
+ this.start = System.currentTimeMillis();
+ this.blip = estimate < 100 ? 1 : estimate / 80;
+ System.out.print(name);
+ }
+
+ public void skip() {
+ System.out.println(" ... skipped");
+ }
+
+ public void scan() {
+ System.out.println("");
+ System.out.print(" Scan :");
+ counter = 0;
+ }
+
+ public void process() {
+ System.out.println("");
+ System.out.print(" Build:");
+ counter = 0;
+ processed = true;
+ }
+
+ public void step() {
+ counter++;
+ if (counter % blip == 0) {
+ System.out.print(".");
+ }
+ }
+
+ public void done() {
+ if (counter > 0) {
+ System.out.println("");
+ }
+ if (processed) {
+ long elapsed = System.currentTimeMillis()-start;
+ if (elapsed > 3000) {
+ System.out.println(" Finished: "+counter+" rows, "+Utilities.describeDuration(elapsed)+" ("+(counter/(elapsed/1000))+" rows/msec)");
+ } else {
+ System.out.println(" Finished: "+counter+" rows, "+Utilities.describeDuration(elapsed));
+ }
+ } else {
+ System.out.println(" Finished: "+counter+" rows");
+ }
+ }
+
+
+ public void error(String e) {
+ System.out.println("error: "+e);
+ System.out.println("row: "+counter);
+ throw new Error(e);
+ }
+
+ }
private Connection con;
private Map relationships = new HashMap<>();
+ private Map vocabularies = new HashMap<>();
+ private Map domains = new HashMap<>();
+ private Map classes = new HashMap<>();
public static void main(String[] args) throws Exception {
- new OMOPImporter().process("/Users/grahamegrieve/Downloads/vocabulary_download_v5_{97cc5432-0dc9-4f14-9da2-d0624129d2f7}_1688068174909");
+ new OMOPImporter().process(args[0], args[1]);
+ // "/Users/grahamegrieve/Downloads/vocabulary_download_v5_{97cc5432-0dc9-4f14-9da2-d0624129d2f7}_1688068174909");
+ // /Users/grahamegrieve/temp/omop/omop.db
}
- private void process(String folder) throws ClassNotFoundException, SQLException, FHIRException, FileNotFoundException, IOException {
- connect();
+ private void process(String folder, String dest) throws ClassNotFoundException, SQLException, FHIRException, FileNotFoundException, IOException {
+ connect(dest);
- loadRelationships(folder, true);
- processVocabularies(folder, false);
- processDomains(folder, false);
- processConceptClasses(folder, false);
+ processRelationships(folder, true);
+ processVocabularies(folder, true);
+ processDomains(folder, true);
+ processConceptClasses(folder, true);
+ processConcepts(folder, true);
+ processConceptSynonyms(folder, true);
+ processConceptRelationships(folder, true);
+ // disabled - don't consume space that isn't required
processDrugStrength(folder, false);
- processConcepts(folder, false);
- processConceptRelationships(folder, false);
- processConceptSynonyms(folder, false);
processConceptAncestors(folder, false);
}
- private void loadRelationships(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
+ private void connect(String dest) throws SQLException, ClassNotFoundException {
+ // Class.forName("com.mysql.jdbc.Driver");
+ // con = DriverManager.getConnection("jdbc:mysql://localhost:3306/omop?useSSL=false","root",{pwd});
+ new File("/Users/grahamegrieve/temp/omop/omop.db").delete();
+ con = DriverManager.getConnection("jdbc:sqlite:"+dest);
+ }
+ private void processRelationships(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
+ Tracker t = new Tracker("Relationships", 700);
CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "RELATIONSHIP.csv")));
csv.setDelimiter('\t');
csv.readHeaders();
csv.setDoingQuotes(false);
- int i = 0;
- Statement stmt = con.createStatement();
- stmt.executeUpdate("delete from Relationships");
+ int lid = 0;
+ int lname = 0;
+
+ t.scan();
while (csv.line()) {
relationships.put(csv.cell("relationship_id"), csv.cell("relationship_concept_id"));
- if (process) {
- String sql = "INSERT INTO `omop`.`Relationships` (`relationship_concept_id`, `relationship_id`, `relationship_name`, `is_hierarchical`, `defines_ancestry`, `reverse_relationship_id`) VALUES ("+
- sw(csv.cell("relationship_concept_id"))+", "+
- sw(csv.cell("relationship_id"))+", "+
- sw(csv.cell("relationship_name"))+", "+
- sw(csv.cell("is_hierarchical"))+", "+
- sw(csv.cell("defines_ancestry"))+", "+
- sw(csv.cell("reverse_relationship_id"))+")";
- try {
- stmt.executeUpdate(sql);
- } catch (Exception e) {
- System.out.println("error: "+e.getMessage());
- System.out.println("i: "+i);
- // System.out.println("sql: "+sql);
- }
- }
- i++;
- if (i % 1000 == 0) {
- System.out.println(i);
- }
+
+ lid = max(lid, csv.cell("relationship_id"));
+ lname = max(lname, csv.cell("relationship_name"));
+ t.step();
}
- csv.close();
-
+
+ csv.close();
+ if (process) {
+ t.process();
+ Statement stmt = con.createStatement();
+ stmt.execute("CREATE TABLE Relationships (\r\n"+
+ "`relationship_concept_id` bigint NOT NULL,\r\n"+
+ "`relationship_id` varchar("+lid+") DEFAULT NULL,\r\n"+
+ "`relationship_name` varchar("+lname+") DEFAULT NULL,\r\n"+
+ "`is_hierarchical` int DEFAULT NULL,\r\n"+
+ "`defines_ancestry` int DEFAULT NULL,\r\n"+
+ "`reverse_relationship_id` varchar(45) DEFAULT NULL,\r\n"+
+ "PRIMARY KEY (`relationship_concept_id`))\r\n");
+
+ stmt.execute("Create Index `RelationshipsId` on Relationships (`relationship_id`)");
+ stmt.execute("Create Index`RelationshipsReverse` on Relationships (`reverse_relationship_id`)");
+
+ csv = new CSVReader(new FileInputStream(Utilities.path(folder, "RELATIONSHIP.csv")));
+ csv.setDelimiter('\t');
+ csv.readHeaders();
+ csv.setDoingQuotes(false);
+
+ while (csv.line()) {
+ relationships.put(csv.cell("relationship_id"), csv.cell("relationship_concept_id"));
+ if (process) {
+ String sql = "INSERT INTO `Relationships` (`relationship_concept_id`, `relationship_id`, `relationship_name`, `is_hierarchical`, `defines_ancestry`, `reverse_relationship_id`) VALUES ("+
+ sw(csv.cell("relationship_concept_id"))+", "+
+ sw(csv.cell("relationship_id"))+", "+
+ sw(csv.cell("relationship_name"))+", "+
+ sw(csv.cell("is_hierarchical"))+", "+
+ sw(csv.cell("defines_ancestry"))+", "+
+ sw(csv.cell("reverse_relationship_id"))+")";
+ try {
+ stmt.executeUpdate(sql);
+ } catch (Exception e) {
+ t.error(e.getMessage());
+ }
+ }
+ t.step();
+ }
+ csv.close();
+ }
+ t.done();
+ }
+
+ private int max(int lid, String cell) {
+ int i = cell == null? 0 : cell.length();
+ return i > lid ? i : lid;
}
private void processVocabularies(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
- if (!process) {
- return;
- }
-
+ Tracker t = new Tracker("Vocabularies", 60);
CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "VOCABULARY.csv")));
csv.setDelimiter('\t');
csv.readHeaders();
csv.setDoingQuotes(false);
- int i = 0;
- Statement stmt = con.createStatement();
- stmt.executeUpdate("delete from Vocabularies");
+ int lid = 0;
+ int lname = 0;
+ int lref = 0;
+ int lver = 0;
+ t.scan();
while (csv.line()) {
- String sql = "INSERT INTO `omop`.`Vocabularies` (`vocabulary_concept_id`, `vocabulary_id`, `vocabulary_name`, `vocabulary_reference`, `vocabulary_version`) VALUES ("+
- sw(csv.cell("vocabulary_concept_id"))+", "+
- sw(csv.cell("vocabulary_id"))+", "+
- sw(csv.cell("vocabulary_name"))+", "+
- sw(csv.cell("vocabulary_reference"))+", "+
- sw(csv.cell("vocabulary_version"))+")";
- try {
- stmt.executeUpdate(sql);
- } catch (Exception e) {
- System.out.println("error: "+e.getMessage());
- System.out.println("i: "+i);
- // System.out.println("sql: "+sql);
- }
- i++;
- if (i % 1000 == 0) {
- System.out.println(i);
- }
+ vocabularies.put(csv.cell("vocabulary_id"), csv.cell("vocabulary_concept_id"));
+
+ lid = max(lid, csv.cell("vocabulary_id"));
+ lname = max(lname, csv.cell("vocabulary_name"));
+ lref = max(lref, csv.cell("vocabulary_reference"));
+ lver = max(lver, csv.cell("vocabulary_version"));
+ t.step();
}
- csv.close();
+ csv.close();
+
+ if (process) {
+ t.process();
+ Statement stmt = con.createStatement();
+
+ stmt.execute("CREATE TABLE `Vocabularies` (\r\n"+
+ " `vocabulary_concept_id` bigint NOT NULL,\r\n"+
+ " `vocabulary_id` varchar("+lid+") DEFAULT NULL,\r\n"+
+ " `vocabulary_name` varchar("+lname+") DEFAULT NULL,\r\n"+
+ " `vocabulary_reference` varchar("+lref+") DEFAULT NULL,\r\n"+
+ " `vocabulary_version` varchar("+lver+") DEFAULT NULL,\r\n"+
+ " PRIMARY KEY (`vocabulary_concept_id`)\r\n"+
+ ") \r\n"+
+ "\r\n");
+ stmt.execute("CREATE INDEX `VocabulariesId` on Vocabularies (`vocabulary_id`)");
+
+ csv = new CSVReader(new FileInputStream(Utilities.path(folder, "VOCABULARY.csv")));
+ csv.setDelimiter('\t');
+ csv.readHeaders();
+ csv.setDoingQuotes(false);
+ while (csv.line()) {
+ String sql = "INSERT INTO `Vocabularies` (`vocabulary_concept_id`, `vocabulary_id`, `vocabulary_name`, `vocabulary_reference`, `vocabulary_version`) VALUES ("+
+ sw(csv.cell("vocabulary_concept_id"))+", "+
+ sw(csv.cell("vocabulary_id"))+", "+
+ sw(csv.cell("vocabulary_name"))+", "+
+ sw(csv.cell("vocabulary_reference"))+", "+
+ sw(csv.cell("vocabulary_version"))+")";
+ try {
+ stmt.executeUpdate(sql);
+ } catch (Exception e) {
+ t.error(e.getMessage());
+ }
+ t.step();
+ }
+ csv.close();
+ }
+ t.done();
+ }
+
+
+ private void processDomains(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
+ Tracker t = new Tracker("Domains", 50);
+
+ CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "DOMAIN.csv")));
+ csv.setDelimiter('\t');
+ csv.readHeaders();
+ csv.setDoingQuotes(false);
+ int lid = 0;
+ int lname = 0;
+ t.scan();
+ while (csv.line()) {
+
+ domains.put(csv.cell("domain_id"), csv.cell("domain_concept_id"));
+
+ lid = max(lid, csv.cell("domain_id"));
+ lname = max(lname, csv.cell("domain_name"));
+ t.step();
+ }
+ csv.close();
+
+ if (process) {
+ t.process();
+ Statement stmt = con.createStatement();
+
+ stmt.execute("CREATE TABLE `Domains` (\r\n"+
+ " `domain_concept_id` bigint NOT NULL,\r\n"+
+ " `domain_id` varchar("+lid+") DEFAULT NULL,\r\n"+
+ " `domain_name` varchar("+lname+") DEFAULT NULL,\r\n"+
+ " PRIMARY KEY (`domain_concept_id`)\r\n"+
+ ") \r\n"+
+ "\r\n");
+
+ stmt.execute("CREATE INDEX `DomainId` on Domains (`domain_id`)");
+
+ csv = new CSVReader(new FileInputStream(Utilities.path(folder, "DOMAIN.csv")));
+ csv.setDelimiter('\t');
+ csv.readHeaders();
+ csv.setDoingQuotes(false);
+ while (csv.line()) {
+ String sql = "INSERT INTO `Domains` (`domain_concept_id`, `domain_id`, `domain_name`) VALUES ("+
+ sw(csv.cell("domain_concept_id"))+", "+
+ sw(csv.cell("domain_id"))+", "+
+ sw(csv.cell("domain_name"))+")";
+ try {
+ stmt.executeUpdate(sql);
+ } catch (Exception e) {
+ t.error(e.getMessage());
+ }
+ t.step();
+ }
+ csv.close();
+ }
+ t.done();
+ }
+
+ private void processConceptClasses(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
+ Tracker t = new Tracker("ConceptClasses", 400);
+
+ CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT_CLASS.csv")));
+ csv.setDelimiter('\t');
+ csv.readHeaders();
+ csv.setDoingQuotes(false);
+ int lid = 0;
+ int lname = 0;
+ t.scan();
+ while (csv.line()) {
+ classes.put(csv.cell("concept_class_id"), csv.cell("concept_class_concept_id"));
+
+ lid = max(lid, csv.cell("concept_class_id"));
+ lname = max(lname, csv.cell("concept_class_name"));
+ t.step();
+ }
+ csv.close();
+
+ if (process) {
+ t.process();
+ Statement stmt = con.createStatement();
+ stmt.execute("CREATE TABLE `ConceptClasses` (\r\n"+
+ " `concept_class_concept_id` bigint NOT NULL,\r\n"+
+ " `concept_class_id` varchar("+lid+") DEFAULT NULL,\r\n"+
+ " `concept_class_name` varchar("+lname+") DEFAULT NULL,\r\n"+
+ " PRIMARY KEY (`concept_class_concept_id`)\r\n"+
+ ") \r\n"+
+ "\r\n");
+ csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT_CLASS.csv")));
+ csv.setDelimiter('\t');
+ csv.readHeaders();
+ csv.setDoingQuotes(false);
+
+ while (csv.line()) {
+ String sql = "INSERT INTO `ConceptClasses` (`concept_class_concept_id`, `concept_class_id`, `concept_class_name`) VALUES ("+
+ sw(csv.cell("concept_class_concept_id"))+", "+
+ sw(csv.cell("concept_class_id"))+", "+
+ sw(csv.cell("concept_class_name"))+")";
+ try {
+ stmt.executeUpdate(sql);
+ } catch (Exception e) {
+ t.error(e.getMessage());
+ }
+ t.step();
+ }
+ csv.close();
+ }
+ t.done();
+
}
private void processDrugStrength(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
+ Tracker t = new Tracker("DrugStrengths", 3000000);
if (!process) {
+ t.skip();
return;
}
@@ -119,253 +362,316 @@ public class OMOPImporter {
csv.setDelimiter('\t');
csv.readHeaders();
csv.setDoingQuotes(false);
- int i = 0;
- Statement stmt = con.createStatement();
- stmt.executeUpdate("delete from DrugStrengths");
+ int lreason = 0;
+ int lamount1 = 0;
+ int lnum1 = 0;
+ int lden1 = 0;
+ int lamount2 = 0;
+ int lnum2 = 0;
+ int lden2 = 0;
+ t.scan();
while (csv.line()) {
- String sql = "INSERT INTO `omop`.`DrugStrengths` (`drug_concept_id`, `ingredient_concept_id`, `amount_value`, `amount_unit_concept_id`, `numerator_value`, `numerator_unit_concept_id`, `denominator_value`, "
- + "`denominator_unit_concept_id`, `box_size`, `valid_start_date`, `valid_end_date`, `invalid_reason`) VALUES ("+
- sw(csv.cell("drug_concept_id"))+", "+
- sw(csv.cell("ingredient_concept_id"))+", "+
- sw(csv.cell("amount_value"))+", "+
- sw(csv.cell("amount_unit_concept_id"))+", "+
- sw(csv.cell("numerator_value"))+", "+
- sw(csv.cell("numerator_unit_concept_id"))+", "+
- sw(csv.cell("denominator_value"))+", "+
- sw(csv.cell("denominator_unit_concept_id"))+", "+
- sw(csv.cell("box_size"))+", "+
- sw(csv.cell("valid_start_date"))+", "+
- sw(csv.cell("valid_end_date"))+", "+
- sw(csv.cell("invalid_reason"))+")";
- try {
- stmt.executeUpdate(sql);
- } catch (Exception e) {
- System.out.println("error: "+e.getMessage());
- System.out.println("i: "+i);
- System.out.println("sql: "+sql);
- }
- i++;
- if (i % 100 == 0) {
- System.out.println(i);
- }
+ lreason = max(lreason, csv.cell("invalid_reason"));
+ lamount1 = dmax1(lamount1, csv.cell("amount_value"));
+ lamount2 = dmax2(lamount2, csv.cell("amount_value"));
+ lnum1 = dmax1(lnum1, csv.cell("numerator_value"));
+ lnum2 = dmax2(lnum2, csv.cell("numerator_value"));
+ lden1 = dmax1(lden1, csv.cell("denominator_value"));
+ lden2 = dmax2(lden2, csv.cell("denominator_value"));
+ t.step();
}
- csv.close();
- }
-
+ csv.close();
+ t.process();
- private void processDomains(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
- if (!process) {
- return;
- }
-
- CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "DOMAIN.csv")));
+ Statement stmt = con.createStatement();
+ stmt.execute("CREATE TABLE `DrugStrengths` (\r\n"+
+ " `drug_concept_id` bigint NOT NULL,\r\n"+
+ " `ingredient_concept_id` bigint NOT NULL,\r\n"+
+ " `amount_value` decimal("+lamount1+","+lamount2+") DEFAULT NULL,\r\n"+
+ " `amount_unit_concept_id` bigint DEFAULT NULL,\r\n"+
+ " `numerator_value` decimal("+lnum1+","+lnum2+") DEFAULT NULL,\r\n"+
+ " `numerator_unit_concept_id` bigint DEFAULT NULL,\r\n"+
+ " `denominator_value` decimal("+lden1+","+lden2+") DEFAULT NULL,\r\n"+
+ " `denominator_unit_concept_id` bigint DEFAULT NULL,\r\n"+
+ " `box_size` int DEFAULT NULL,\r\n"+
+ " `valid_start_date` date DEFAULT NULL,\r\n"+
+ " `valid_end_date` date DEFAULT NULL,\r\n"+
+ " `invalid_reason` varchar("+lreason+") DEFAULT NULL,\r\n"+
+ " PRIMARY KEY (`drug_concept_id`,`ingredient_concept_id`)\r\n"+
+ ") \r\n"+
+ "\r\n");
+ csv = new CSVReader(new FileInputStream(Utilities.path(folder, "DRUG_STRENGTH.csv")));
csv.setDelimiter('\t');
csv.readHeaders();
csv.setDoingQuotes(false);
- int i = 0;
- Statement stmt = con.createStatement();
- stmt.executeUpdate("delete from Domains");
+ PreparedStatement pstmt = con.prepareStatement("INSERT INTO `DrugStrengths` (`drug_concept_id`, `ingredient_concept_id`, `amount_value`, `amount_unit_concept_id`, `numerator_value`, `numerator_unit_concept_id`, `denominator_value`, "
+ + "`denominator_unit_concept_id`, `box_size`, `valid_start_date`, `valid_end_date`, `invalid_reason`) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
while (csv.line()) {
- String sql = "INSERT INTO `omop`.`Domains` (`domain_concept_id`, `domain_id`, `domain_name`) VALUES ("+
- sw(csv.cell("domain_concept_id"))+", "+
- sw(csv.cell("domain_id"))+", "+
- sw(csv.cell("domain_name"))+")";
try {
- stmt.executeUpdate(sql);
+ pstmt.setString(1, csv.cell("drug_concept_id"));
+ pstmt.setString(2, csv.cell("ingredient_concept_id"));
+ pstmt.setString(3, csv.cell("amount_value"));
+ pstmt.setString(4, csv.cell("amount_unit_concept_id"));
+ pstmt.setString(5, csv.cell("numerator_value"));
+ pstmt.setString(6, csv.cell("numerator_unit_concept_id"));
+ pstmt.setString(7, csv.cell("denominator_value"));
+ pstmt.setString(8, csv.cell("denominator_unit_concept_id"));
+ pstmt.setString(9, csv.cell("box_size"));
+ pstmt.setString(10, date(csv.cell("valid_start_date")));
+ pstmt.setString(11, date(csv.cell("valid_end_date")));
+ pstmt.setString(11, csv.cell("invalid_reason"));
+ pstmt.executeUpdate();
} catch (Exception e) {
- System.out.println("error: "+e.getMessage());
- System.out.println("i: "+i);
- // System.out.println("sql: "+sql);
- }
- i++;
- if (i % 1000 == 0) {
- System.out.println(i);
+ t.error(e.getMessage());
}
+ t.step();
}
- csv.close();
- }
-
+ csv.close();
+ t.done();
- private void processConceptClasses(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
- if (!process) {
- return;
- }
-
- CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT_CLASS.csv")));
- csv.setDelimiter('\t');
- csv.readHeaders();
- csv.setDoingQuotes(false);
- int i = 0;
- Statement stmt = con.createStatement();
- stmt.executeUpdate("delete from ConceptClasses");
- while (csv.line()) {
- String sql = "INSERT INTO `omop`.`ConceptClasses` (`concept_class_concept_id`, `concept_class_id`, `concept_class_name`) VALUES ("+
- sw(csv.cell("concept_class_concept_id"))+", "+
- sw(csv.cell("concept_class_id"))+", "+
- sw(csv.cell("concept_class_name"))+")";
- try {
- stmt.executeUpdate(sql);
- } catch (Exception e) {
- System.out.println("error: "+e.getMessage());
- System.out.println("i: "+i);
- // System.out.println("sql: "+sql);
- }
- i++;
- if (i % 1000 == 0) {
- System.out.println(i);
- }
- }
- csv.close();
}
-
-
+
+ private int dmax1(int lid, String cell) {
+ int i = cell == null? 0 : cell.indexOf('.');
+ return i > lid ? i : lid;
+ }
+
+ private int dmax2(int lid, String cell) {
+ int i = cell == null? 0 : cell.length() - cell.indexOf('.') - 1;
+ return i > lid ? i : lid;
+ }
private void processConcepts(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
+ Tracker t = new Tracker("Concepts", 5617348);
if (!process) {
+ t.skip();
return;
}
+
CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT.csv")));
csv.setDelimiter('\t');
csv.readHeaders();
csv.setDoingQuotes(false);
- int i = 0;
- Statement stmt = con.createStatement();
+ int lname = 0;
+ int lstd = 0;
+ int lcode = 0;
+ int lreason = 0;
+ t.scan();
+ while (csv.line()) {
+ lname = max(lname, csv.cell("concept_name"));
+ lstd = max(lstd, csv.cell("standard_concept"));
+ lcode = max(lcode, csv.cell("concept_code"));
+ lreason = max(lreason, csv.cell("invalid_reason"));
+ t.step();
+ }
+ csv.close();
+ t.process();
+
+ Statement stmt = con.createStatement();
+ stmt.execute("CREATE TABLE `Concepts` (\r\n"+
+ " `concept_id` bigint NOT NULL,\r\n"+
+ " `concept_name` varchar("+lname+") DEFAULT NULL,\r\n"+
+ " `domain_id` bigint DEFAULT NULL,\r\n"+
+ " `vocabulary_id` bigint DEFAULT NULL,\r\n"+
+ " `concept_class_id` bigint DEFAULT NULL,\r\n"+
+ " `standard_concept` varchar("+lstd+") DEFAULT NULL,\r\n"+
+ " `concept_code` varchar("+lcode+") DEFAULT NULL,\r\n"+
+ " `valid_start_date` date DEFAULT NULL,\r\n"+
+ " `valid_end_date` date DEFAULT NULL,\r\n"+
+ " `invalid_reason` varchar("+lreason+") DEFAULT NULL,\r\n"+
+ " PRIMARY KEY (`concept_id`)\r\n"+
+ " ) \r\n"+
+ "\r\n");
+
+ stmt.execute("CREATE INDEX `ConceptDomain` on Concepts (`domain_id`)");
+ stmt.execute("CREATE INDEX `ConceptVocabulary` on Concepts (`vocabulary_id`,`concept_code`)");
+ stmt.execute("CREATE INDEX `ConceptClass` on Concepts (`concept_class_id`)");
+
+ csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT.csv")));
+ csv.setDelimiter('\t');
+ csv.readHeaders();
+ csv.setDoingQuotes(false);
+
+ PreparedStatement pstmt = con.prepareStatement(
+ "INSERT INTO `Concepts` (`concept_id`, `concept_name`, `domain_id`, `vocabulary_id`, `concept_class_id`, `standard_concept`, `concept_code`, `valid_start_date`, `valid_end_date`, `invalid_reason`) "+
+ "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
while (csv.line()) {
- String sql = "INSERT INTO `omop`.`Concepts` (`concept_id`, `concept_name`, `domain_id`, `vocabulary_id`, `concept_class_id`, `standard_concept`, `concept_code`, `valid_start_date`, `valid_end_date`, `invalid_reason`) VALUES ("+
- sw(csv.cell("concept_id"))+", "+
- sw(csv.cell("concept_name"))+", "+
- sw(csv.cell("domain_id"))+", "+
- sw(csv.cell("vocabulary_id"))+", "+
- sw(csv.cell("concept_class_id"))+", "+
- sw(csv.cell("standard_concept"))+", "+
- sw(csv.cell("concept_code"))+", "+
- sw(csv.cell("valid_start_date"))+", "+
- sw(csv.cell("valid_end_date"))+", "+
- sw(csv.cell("invalid_reason"))+")";
try {
- stmt.executeUpdate(sql);
+ pstmt.setString(1, csv.cell("concept_id"));
+ pstmt.setString(2, csv.cell("concept_name"));
+ pstmt.setString(3, domains.get(csv.cell("domain_id")));
+ pstmt.setString(4, vocabularies.get(csv.cell("vocabulary_id")));
+ pstmt.setString(5, classes.get(csv.cell("concept_class_id")));
+ pstmt.setString(6, csv.cell("standard_concept"));
+ pstmt.setString(7, csv.cell("concept_code"));
+ pstmt.setString(8, date(csv.cell("valid_start_date")));
+ pstmt.setString(9, date(csv.cell("valid_end_date")));
+ pstmt.setString(10, csv.cell("invalid_reason"));
+ pstmt.executeUpdate();
} catch (Exception e) {
- System.out.println("error: "+e.getMessage());
- System.out.println("i: "+i);
-// System.out.println("sql: "+sql);
- }
- i++;
- if (i % 1000 == 0) {
- System.out.println(i);
+ t.error(e.getMessage());
}
+ t.step();
}
csv.close();
-
+ t.done();
}
private void processConceptSynonyms(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
+ Tracker t = new Tracker("ConceptSynonyms", 1933498);
if (!process) {
+ t.skip();
return;
- }
+ }
+ t.scan();
+
CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT_SYNONYM.csv")));
csv.setDelimiter('\t');
csv.readHeaders();
csv.setDoingQuotes(false);
- int i = 0;
- int ec = 0;
- Statement stmt = con.createStatement();
-
- stmt.executeUpdate("delete from ConceptSynonyms");
+ int lname = 0;
while (csv.line()) {
- String sql = "INSERT INTO `omop`.`ConceptSynonyms` (`concept_id`, `concept_synonym_name`, `language_concept_id`) VALUES ("+
- sw(csv.cell("concept_id"))+", "+
- sw(csv.cell("concept_synonym_name"))+", "+
- sw(csv.cell("language_concept_id"))+")";
- try {
- stmt.executeUpdate(sql);
- } catch (Exception e) {
- System.out.println("error: "+e.getMessage());
- System.out.println("i: "+i);
- System.out.println("sql: "+sql);
- ec++;
- }
- i++;
- if (i % 1000 == 0) {
- System.out.println(i);
- }
+ lname = max(lname, csv.cell("concept_synonym_name"));
+ t.step();
}
- csv.close();
- System.out.println("Finished. "+i+" rows, "+ec+" errors");
- }
-
+ csv.close();
+ t.process();
- private void processConceptAncestors(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
- if (!process) {
- return;
- }
- CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT_ANCESTOR"
- + ".csv")));
+ Statement stmt = con.createStatement();
+ stmt.execute("CREATE TABLE `ConceptSynonyms` (\r\n"+
+ " `concept_id` bigint NOT NULL,\r\n"+
+ " `concept_synonym_name` varchar("+lname+") DEFAULT NULL,\r\n"+
+ " `language_concept_id` bigint DEFAULT NULL\r\n"+
+ ") \r\n"+
+ "\r\n");
+ stmt.execute("CREATE INDEX `SynonymId` on ConceptSynonyms (`concept_id`)");
+ stmt.execute("CREATE INDEX `SynonymLang` on ConceptSynonyms (`language_concept_id`)");
+
+ csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT_SYNONYM.csv")));
csv.setDelimiter('\t');
csv.readHeaders();
csv.setDoingQuotes(false);
- int i = 0;
- int ec = 0;
- Statement stmt = con.createStatement();
-
- stmt.executeUpdate("delete from ConceptAncestors");
+
+ PreparedStatement pstmt = con.prepareStatement("INSERT INTO `ConceptSynonyms` (`concept_id`, `concept_synonym_name`, `language_concept_id`) VALUES (?, ?, ?)");
while (csv.line()) {
- String sql = "INSERT INTO `omop`.`ConceptAncestors` (`ancestor_concept_id`, `descendant_concept_id`, `min_levels_of_separation`, `max_levels_of_separation`) VALUES ("+
- sw(csv.cell("ancestor_concept_id"))+", "+
- sw(csv.cell("descendant_concept_id"))+", "+
- sw(csv.cell("min_levels_of_separation"))+", "+
- sw(csv.cell("max_levels_of_separation"))+")";
try {
- stmt.executeUpdate(sql);
+ pstmt.setString(1, csv.cell("concept_id"));
+ pstmt.setString(2, csv.cell("concept_synonym_name"));
+ pstmt.setString(3, csv.cell("language_concept_id"));
+ pstmt.executeUpdate();
} catch (Exception e) {
- System.out.println("error: "+e.getMessage());
- System.out.println("i: "+i);
- System.out.println("sql: "+sql);
- ec++;
- }
- i++;
- if (i % 1000 == 0) {
- System.out.println(i);
+ t.error(e.getMessage());
}
+ t.step();
}
- csv.close();
- System.out.println("Finished. "+i+" rows, "+ec+" errors");
+ csv.close();
+ t.done();
}
-
-
- private void processConceptRelationships(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
+
+
+ private void processConceptAncestors(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
+ Tracker t = new Tracker("ConceptAncestors", 67425885);
if (!process) {
+ t.skip();
return;
}
+
+ t.process();
+ CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT_ANCESTOR.csv")));
+ csv.setDelimiter('\t');
+ csv.readHeaders();
+ csv.setDoingQuotes(false);
+ con.createStatement().execute("CREATE TABLE `ConceptAncestors` (\r\n"+
+ " `ancestor_concept_id` bigint NOT NULL,\r\n"+
+ " `descendant_concept_id` bigint NOT NULL,\r\n"+
+ " `min_levels_of_separation` int DEFAULT NULL,\r\n"+
+ " `max_levels_of_separation` int DEFAULT NULL,\r\n"+
+ " PRIMARY KEY (`ancestor_concept_id`,`descendant_concept_id`)\r\n"+
+ " ) \r\n"+
+ "\r\n");
+
+ PreparedStatement pstmt = con.prepareStatement("INSERT INTO `ConceptAncestors` (`ancestor_concept_id`, `descendant_concept_id`, `min_levels_of_separation`, `max_levels_of_separation`) VALUES (?, ?, ?, ?)");
+ while (csv.line()) {
+ try {
+ pstmt.setString(1, csv.cell("ancestor_concept_id"));
+ pstmt.setString(2, csv.cell("descendant_concept_id"));
+ pstmt.setString(3, csv.cell("min_levels_of_separation"));
+ pstmt.setString(4, csv.cell("max_levels_of_separation"));
+ pstmt.executeUpdate();
+ } catch (Exception e) {
+ t.error(e.getMessage());
+ }
+ t.step();
+ }
+ csv.close();
+ t.done();
+ }
+
+
+ private void processConceptRelationships(String folder, boolean process) throws FHIRException, FileNotFoundException, IOException, SQLException {
+ Tracker t = new Tracker("ConceptRelationships", 47000000);
+ if (!process) {
+ t.skip();
+ return;
+ }
+
+ t.scan();
CSVReader csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT_RELATIONSHIP.csv")));
csv.setDelimiter('\t');
csv.readHeaders();
csv.setDoingQuotes(false);
- int i = 0;
- Statement stmt = con.createStatement();
+ int lreason = 0;
while (csv.line()) {
- String sql = "INSERT INTO `omop`.`ConceptRelationships` (`concept_id_1`, `concept_id_2`, `relationship_id`, `valid_start_date`, `valid_end_date`, `invalid_reason`) VALUES ("+
- sw(csv.cell("concept_id_1"))+", "+
- sw(csv.cell("concept_id_2"))+", "+
- sw(relationships.get(csv.cell("relationship_id")))+", "+
- sw(csv.cell("valid_start_date"))+", "+
- sw(csv.cell("valid_end_date"))+", "+
- sw(csv.cell("invalid_reason"))+")";
- try {
- stmt.executeUpdate(sql);
- } catch (Exception e) {
- System.out.println("error: "+e.getMessage());
- System.out.println("i: "+i);
-// System.out.println("sql: "+sql);
- }
- i++;
- if (i % 100 == 0) {
- System.out.println(i);
- }
+ lreason = max(lreason, csv.cell("invalid_reason"));
+ t.step();
}
- csv.close();
-
+ csv.close();
+ t.process();
+
+ Statement stmt = con.createStatement();
+ stmt.execute("CREATE TABLE `ConceptRelationships` (\r\n"+
+ " `concept_id_1` bigint NOT NULL,\r\n"+
+ " `concept_id_2` bigint NOT NULL,\r\n"+
+ " `relationship_id` bigint NOT NULL,\r\n"+
+ " `valid_start_date` date DEFAULT NULL,\r\n"+
+ " `valid_end_date` date DEFAULT NULL,\r\n"+
+ " `invalid_reason` varchar("+lreason+") DEFAULT NULL)\r\n"+
+ " \r\n");
+
+ stmt.execute("CREATE INDEX `Reverse` on ConceptRelationships (`concept_id_2`,`concept_id_1`,`relationship_id`)");
+ stmt.execute("CREATE INDEX `Forward` on ConceptRelationships (`concept_id_1`,`concept_id_2`,`relationship_id`)");
+// stmt.execute("CREATE INDEX `type1` on ConceptRelationships (`relationship_id`,`concept_id_1`,`concept_id_2`)");
+// stmt.execute("CREATE INDEX `type2` on ConceptRelationships (`relationship_id`,`concept_id_2`,`concept_id_1`)");
+
+ csv = new CSVReader(new FileInputStream(Utilities.path(folder, "CONCEPT_RELATIONSHIP.csv")));
+ csv.setDelimiter('\t');
+ csv.readHeaders();
+ csv.setDoingQuotes(false);
+
+ PreparedStatement pstmt = con.prepareStatement("INSERT INTO `ConceptRelationships` (`concept_id_1`, `concept_id_2`, `relationship_id`, `valid_start_date`, `valid_end_date`, `invalid_reason`) VALUES (?, ?, ?, ?, ?, ?)");
+ while (csv.line()) {
+ try {
+ pstmt.setString(1, csv.cell("concept_id_1"));
+ pstmt.setString(2, csv.cell("concept_id_2"));
+ pstmt.setString(3, relationships.get(csv.cell("relationship_id")));
+ pstmt.setString(4, csv.cell("valid_start_date"));
+ pstmt.setString(5, date(csv.cell("valid_end_date")));
+ pstmt.setString(6, date(csv.cell("invalid_reason")));
+ pstmt.executeUpdate();
+ } catch (Exception e) {
+ t.error(e.getMessage());
+ }
+ t.step();
+ }
+ csv.close();
+ t.done();
+
+ }
+
+
+ private String date(String cell) {
+ return cell;
}
-
private String sw(String value) {
if (value == null) {
@@ -383,10 +689,5 @@ public class OMOPImporter {
return b.toString();
}
- private void connect() throws SQLException, ClassNotFoundException {
-// Class.forName("com.mysql.jdbc.Driver");
- con = DriverManager.getConnection("jdbc:mysql://localhost:3306/omop?useSSL=false","root","@AZEq|OzHLl1/[50v[CI");
-
- }
}
diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/elementmodel/Element.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/elementmodel/Element.java
index 01df46481..3f31e16b0 100644
--- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/elementmodel/Element.java
+++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/elementmodel/Element.java
@@ -325,6 +325,40 @@ public class Element extends Base {
}
}
+ @Override
+ public Base copy() {
+ Element element = new Element(this);
+ this.copyValues(element);
+ return element;
+ }
+
+ @Override
+ public void copyValues(Base dst) {
+ super.copyValues(dst);
+
+ Element dest = (Element) dst;
+ if (comments != null) {
+ dest.comments = new ArrayList<>();
+ dest.comments.addAll(comments);
+ } else {
+ dest.comments = null;
+ }
+ dest.value = value;
+ if (children != null) {
+ dest.children = new ArrayList<>();
+ for (Element child : children) {
+ dest.children.add((Element) child.copy());
+ }
+ } else {
+ dest.children = null;
+ }
+ dest.line = line;
+ dest.col = col;
+ dest.xhtml = xhtml;
+ dest.explicitType = explicitType;
+ }
+
+
@Override
public Base setProperty(int hash, String name, Base value) throws FHIRException {
if ("xhtml".equals(getType()) && (hash == "value".hashCode())) {
diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Base.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Base.java
index b3fc0102e..7cda63e03 100644
--- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Base.java
+++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Base.java
@@ -1032,4 +1032,11 @@ private Map userData;
return null;
}
+
+ public abstract Base copy();
+
+ public void copyValues(Base dst) {
+ }
+
+
}
\ No newline at end of file
diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java
index 792e57a52..0c333343a 100644
--- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java
+++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/ExpressionNode.java
@@ -55,7 +55,7 @@ public class ExpressionNode {
Encode, Decode, Escape, Unescape, Trim, Split, Join, LowBoundary, HighBoundary, Precision,
// Local extensions to FHIRPath
- HtmlChecks1, HtmlChecks2, AliasAs, Alias;
+ HtmlChecks1, HtmlChecks2, AliasAs, Alias, Comparable;
public static Function fromCode(String name) {
if (name.equals("empty")) return Function.Empty;
@@ -117,6 +117,7 @@ public class ExpressionNode {
if (name.equals("htmlChecks")) return Function.HtmlChecks1;
if (name.equals("htmlchecks")) return Function.HtmlChecks1; // support change of care from R3
if (name.equals("htmlChecks2")) return Function.HtmlChecks2;
+ if (name.equals("comparable")) return Function.Comparable;
if (name.equals("encode")) return Function.Encode;
if (name.equals("decode")) return Function.Decode;
if (name.equals("escape")) return Function.Escape;
@@ -225,6 +226,7 @@ public class ExpressionNode {
case Join : return "join";
case HtmlChecks1 : return "htmlChecks";
case HtmlChecks2 : return "htmlChecks2";
+ case Comparable : return "comparable";
case OfType : return "ofType";
case Type : return "type";
case ToInteger : return "toInteger";
diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Tuple.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Tuple.java
index 747038551..1b4510971 100644
--- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Tuple.java
+++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/model/Tuple.java
@@ -80,5 +80,11 @@ public class Tuple extends Base {
return getProperty(name.hashCode(), name, checkValid);
}
+ @Override
+ public Base copy() {
+ Tuple tuple = new Tuple();
+ copyValues(tuple);
+ return tuple;
+ }
}
\ No newline at end of file
diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java
index a5f43da2e..8eaf15d47 100644
--- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java
+++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/FHIRPathEngine.java
@@ -145,6 +145,12 @@ public class FHIRPathEngine {
public String primitiveValue() {
return value;
}
+
+ @Override
+ public Base copy() {
+ throw new Error("Not Implemented");
+ }
+
}
private class ClassTypeInfo extends Base {
@@ -203,6 +209,12 @@ public class FHIRPathEngine {
return instance.fhirType();
}
}
+
+ @Override
+ public Base copy() {
+ throw new Error("Not Implemented");
+ }
+
}
public static class TypedElementDefinition {
@@ -1404,6 +1416,7 @@ public class FHIRPathEngine {
case Join: return checkParamCount(lexer, location, exp, 1);
case HtmlChecks1: return checkParamCount(lexer, location, exp, 0);
case HtmlChecks2: return checkParamCount(lexer, location, exp, 0);
+ case Comparable: return checkParamCount(lexer, location, exp, 1);
case ToInteger: return checkParamCount(lexer, location, exp, 0);
case ToDecimal: return checkParamCount(lexer, location, exp, 0);
case ToString: return checkParamCount(lexer, location, exp, 0);
@@ -3336,6 +3349,8 @@ public class FHIRPathEngine {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case HtmlChecks2 :
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
+ case Comparable :
+ return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case Alias :
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
return anything(CollectionStatus.SINGLETON);
@@ -3552,7 +3567,7 @@ public class FHIRPathEngine {
}
private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException {
- if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time")) {
+ if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time") && !focus.hasType("Quantity")) {
throw makeException(expr, I18nConstants.FHIRPATH_CONTINUOUS_ONLY, name, focus.describe());
}
}
@@ -3642,6 +3657,7 @@ public class FHIRPathEngine {
case Alias : return funcAlias(context, focus, exp);
case HtmlChecks1 : return funcHtmlChecks1(context, focus, exp);
case HtmlChecks2 : return funcHtmlChecks2(context, focus, exp);
+ case Comparable : return funcComparable(context, focus, exp);
case ToInteger : return funcToInteger(context, focus, exp);
case ToDecimal : return funcToDecimal(context, focus, exp);
case ToString : return funcToString(context, focus, exp);
@@ -3879,9 +3895,12 @@ public class FHIRPathEngine {
}
return result;
}
-
+
private List funcLowBoundary(ExecutionContext context, List focus, ExpressionNode expr) {
- if (focus.size() != 1) {
+ if (focus.size() == 0) {
+ return makeNull();
+ }
+ if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "lowBoundary", focus.size());
}
int precision = 0;
@@ -3904,6 +3923,11 @@ public class FHIRPathEngine {
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
+ } else if (base.hasType("Quantity")) {
+ String value = getNamedValue(base, "value");
+ Base v = base.copy();
+ v.setProperty("value", new DecimalType(Utilities.lowBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
+ result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");
}
@@ -3911,7 +3935,10 @@ public class FHIRPathEngine {
}
private List funcHighBoundary(ExecutionContext context, List focus, ExpressionNode expr) {
- if (focus.size() != 1) {
+ if (focus.size() == 0) {
+ return makeNull();
+ }
+ if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "highBoundary", focus.size());
}
int precision = 0;
@@ -3934,6 +3961,11 @@ public class FHIRPathEngine {
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
+ } else if (base.hasType("Quantity")) {
+ String value = getNamedValue(base, "value");
+ Base v = base.copy();
+ v.setProperty("value", new DecimalType(Utilities.highBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
+ result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");
}
@@ -4094,7 +4126,8 @@ public class FHIRPathEngine {
List result = new ArrayList();
if (focus.size() == 1) {
String cnt = focus.get(0).primitiveValue();
- for (String s : cnt.split(param)) {
+ String[] sl = Pattern.compile(param, Pattern.LITERAL).split(cnt);
+ for (String s : sl) {
result.add(new StringType(s));
}
}
@@ -4104,9 +4137,14 @@ public class FHIRPathEngine {
private List funcJoin(ExecutionContext context, List focus, ExpressionNode exp) {
List nl = execute(context, focus, exp.getParameters().get(0), true);
String param = nl.get(0).primitiveValue();
-
+ String param2 = param;
+ if (exp.getParameters().size() == 2) {
+ nl = execute(context, focus, exp.getParameters().get(1), true);
+ param2 = nl.get(0).primitiveValue();
+ }
+
List result = new ArrayList();
- CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(param);
+ CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(param, param2);
for (Base i : focus) {
b.append(i.primitiveValue());
}
@@ -4168,6 +4206,47 @@ public class FHIRPathEngine {
return false;
}
+ private List funcComparable(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException {
+ if (focus.size() != 1 || !(focus.get(0).fhirType().equals("Quantity"))) {
+ return makeBoolean(false);
+ }
+ List nl = execute(context, focus, exp.getParameters().get(0), true);
+ if (nl.size() != 1 || !(nl.get(0).fhirType().equals("Quantity"))) {
+ return makeBoolean(false);
+ }
+ String s1 = getNamedValue(focus.get(0), "system");
+ String u1 = getNamedValue(focus.get(0), "code");
+ String s2 = getNamedValue(nl.get(0), "system");
+ String u2 = getNamedValue(nl.get(0), "code");
+
+ if (s1 == null || s2 == null || !s1.equals(s2)) {
+ return makeBoolean(false);
+ }
+ if (u1 == null || u2 == null) {
+ return makeBoolean(false);
+ }
+ if (u1.equals(u2)) {
+ return makeBoolean(true);
+ }
+ if (s1.equals("http://unitsofmeasure.org") && worker.getUcumService() != null) {
+ try {
+ return makeBoolean(worker.getUcumService().isComparable(u1, u2));
+ } catch (UcumException e) {
+ return makeBoolean(false);
+ }
+ } else {
+ return makeBoolean(false);
+ }
+ }
+
+
+ private String getNamedValue(Base base, String name) {
+ Property p = base.getChildByName(name);
+ if (p.hasValues() && p.getValues().size() == 1) {
+ return p.getValues().get(0).primitiveValue();
+ }
+ return null;
+ }
private boolean checkHtmlNames(XhtmlNode node) {
if (node.getNodeType() == NodeType.Comment) {
diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/GraphQLEngine.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/GraphQLEngine.java
index 9e5b87fc8..d725a887b 100644
--- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/GraphQLEngine.java
+++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/GraphQLEngine.java
@@ -746,6 +746,12 @@ public class GraphQLEngine implements IGraphQLEngine {
return super.getNamedProperty(_hash, _name, _checkValid);
}
}
+
+ @Override
+ public Base copy() {
+ throw new Error("Not Implemented");
+ }
+
}
public static class SearchWrapper extends Base {
@@ -844,6 +850,11 @@ public class GraphQLEngine implements IGraphQLEngine {
return null;
}
+ @Override
+ public Base copy() {
+ throw new Error("Not Implemented");
+ }
+
}
diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/elementmodel/Element.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/elementmodel/Element.java
index 70d68a839..a99554298 100644
--- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/elementmodel/Element.java
+++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/elementmodel/Element.java
@@ -343,6 +343,45 @@ public class Element extends Base {
}
}
+ @Override
+ public Base copy() {
+ Element element = new Element(this);
+ this.copyValues(element);
+ return element;
+ }
+
+ @Override
+ public void copyValues(Base dst) {
+ super.copyValues(dst);
+
+ Element dest = (Element) dst;
+ if (comments != null) {
+ dest.comments = new ArrayList<>();
+ dest.comments.addAll(comments);
+ } else {
+ dest.comments = null;
+ }
+ dest.value = value;
+ if (children != null) {
+ dest.children = new ArrayList<>();
+ for (Element child : children) {
+ dest.children.add((Element) child.copy());
+ }
+ } else {
+ dest.children = null;
+ }
+ dest.line = line;
+ dest.col = col;
+ dest.xhtml = xhtml;
+ dest.explicitType = explicitType;
+ dest.hasParentForValidator = false;
+ dest.path = path;
+ dest.messages = null;
+ dest.prohibited = prohibited;
+ dest.required = required;
+ }
+
+
@Override
public Base setProperty(int hash, String name, Base value) throws FHIRException {
if ("xhtml".equals(getType()) && (hash == "value".hashCode())) {
diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/Base.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/Base.java
index d215d7c05..57c6b3468 100644
--- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/Base.java
+++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/Base.java
@@ -339,6 +339,8 @@ private Map userData;
return null;
}
+ public abstract Base copy();
+
public void copyValues(Base dst) {
}
diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/ExpressionNode.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/ExpressionNode.java
index cc3fb058a..f3e46b586 100644
--- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/ExpressionNode.java
+++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/ExpressionNode.java
@@ -56,7 +56,7 @@ public class ExpressionNode {
Encode, Decode, Escape, Unescape, Trim, Split, Join, LowBoundary, HighBoundary, Precision,
// Local extensions to FHIRPath
- HtmlChecks1, HtmlChecks2, AliasAs, Alias;
+ HtmlChecks1, HtmlChecks2, AliasAs, Alias, Comparable;
public static Function fromCode(String name) {
if (name.equals("empty")) return Function.Empty;
@@ -118,6 +118,7 @@ public class ExpressionNode {
if (name.equals("htmlChecks")) return Function.HtmlChecks1;
if (name.equals("htmlchecks")) return Function.HtmlChecks1; // support change of care from R3
if (name.equals("htmlChecks2")) return Function.HtmlChecks2;
+ if (name.equals("comparable")) return Function.Comparable;
if (name.equals("encode")) return Function.Encode;
if (name.equals("decode")) return Function.Decode;
if (name.equals("escape")) return Function.Escape;
@@ -226,6 +227,7 @@ public class ExpressionNode {
case Join : return "join";
case HtmlChecks1 : return "htmlChecks";
case HtmlChecks2 : return "htmlChecks2";
+ case Comparable : return "comparable";
case OfType : return "ofType";
case Type : return "type";
case ToInteger : return "toInteger";
diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/Tuple.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/Tuple.java
index 048fb1c13..65f1e746f 100644
--- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/Tuple.java
+++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/model/Tuple.java
@@ -98,5 +98,11 @@ public class Tuple extends Base {
return getProperty(name.hashCode(), name, checkValid);
}
+ @Override
+ public Base copy() {
+ Tuple tuple = new Tuple();
+ copyValues(tuple);
+ return tuple;
+ }
}
\ No newline at end of file
diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java
index 15d82473c..603636448 100644
--- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java
+++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/FHIRPathEngine.java
@@ -146,6 +146,11 @@ public class FHIRPathEngine {
public String primitiveValue() {
return value;
}
+
+ @Override
+ public Base copy() {
+ throw new Error("Not Implemented");
+ }
}
private class ClassTypeInfo extends Base {
@@ -204,6 +209,12 @@ public class FHIRPathEngine {
return instance.fhirType();
}
}
+
+ @Override
+ public Base copy() {
+ throw new Error("Not Implemented");
+ }
+
}
public static class TypedElementDefinition {
@@ -1400,6 +1411,7 @@ public class FHIRPathEngine {
case Join: return checkParamCount(lexer, location, exp, 1);
case HtmlChecks1: return checkParamCount(lexer, location, exp, 0);
case HtmlChecks2: return checkParamCount(lexer, location, exp, 0);
+ case Comparable: return checkParamCount(lexer, location, exp, 1);
case ToInteger: return checkParamCount(lexer, location, exp, 0);
case ToDecimal: return checkParamCount(lexer, location, exp, 0);
case ToString: return checkParamCount(lexer, location, exp, 0);
@@ -3332,6 +3344,8 @@ public class FHIRPathEngine {
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case HtmlChecks2 :
return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
+ case Comparable :
+ return new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_Boolean);
case Alias :
checkParamTypes(exp, exp.getFunction().toCode(), paramTypes, new TypeDetails(CollectionStatus.SINGLETON, TypeDetails.FP_String));
return anything(CollectionStatus.SINGLETON);
@@ -3548,7 +3562,7 @@ public class FHIRPathEngine {
}
private void checkContextContinuous(TypeDetails focus, String name, ExpressionNode expr) throws PathEngineException {
- if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time")) {
+ if (!focus.hasNoTypes() && !focus.hasType("decimal") && !focus.hasType("date") && !focus.hasType("dateTime") && !focus.hasType("time") && !focus.hasType("Quantity")) {
throw makeException(expr, I18nConstants.FHIRPATH_CONTINUOUS_ONLY, name, focus.describe());
}
}
@@ -3638,6 +3652,7 @@ public class FHIRPathEngine {
case Alias : return funcAlias(context, focus, exp);
case HtmlChecks1 : return funcHtmlChecks1(context, focus, exp);
case HtmlChecks2 : return funcHtmlChecks2(context, focus, exp);
+ case Comparable : return funcComparable(context, focus, exp);
case ToInteger : return funcToInteger(context, focus, exp);
case ToDecimal : return funcToDecimal(context, focus, exp);
case ToString : return funcToString(context, focus, exp);
@@ -3876,8 +3891,19 @@ public class FHIRPathEngine {
return result;
}
+ private String getNamedValue(Base base, String name) {
+ Property p = base.getChildByName(name);
+ if (p.hasValues() && p.getValues().size() == 1) {
+ return p.getValues().get(0).primitiveValue();
+ }
+ return null;
+ }
+
private List funcLowBoundary(ExecutionContext context, List focus, ExpressionNode expr) {
- if (focus.size() != 1) {
+ if (focus.size() == 0) {
+ return makeNull();
+ }
+ if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "lowBoundary", focus.size());
}
int precision = 0;
@@ -3900,6 +3926,11 @@ public class FHIRPathEngine {
result.add(new DateTimeType(Utilities.lowBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.lowBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
+ } else if (base.hasType("Quantity")) {
+ String value = getNamedValue(base, "value");
+ Base v = base.copy();
+ v.setProperty("value", new DecimalType(Utilities.lowBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
+ result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");
}
@@ -3907,7 +3938,10 @@ public class FHIRPathEngine {
}
private List funcHighBoundary(ExecutionContext context, List focus, ExpressionNode expr) {
- if (focus.size() != 1) {
+ if (focus.size() == 0) {
+ return makeNull();
+ }
+ if (focus.size() > 1) {
throw makeExceptionPlural(focus.size(), expr, I18nConstants.FHIRPATH_FOCUS, "highBoundary", focus.size());
}
int precision = 0;
@@ -3930,6 +3964,11 @@ public class FHIRPathEngine {
result.add(new DateTimeType(Utilities.highBoundaryForDate(base.primitiveValue(), precision == 0 ? 17 : precision)));
} else if (base.hasType("time")) {
result.add(new TimeType(Utilities.highBoundaryForTime(base.primitiveValue(), precision == 0 ? 9 : precision)));
+ } else if (base.hasType("Quantity")) {
+ String value = getNamedValue(base, "value");
+ Base v = base.copy();
+ v.setProperty("value", new DecimalType(Utilities.highBoundaryForDecimal(value, precision == 0 ? 8 : precision)));
+ result.add(v);
} else {
makeException(expr, I18nConstants.FHIRPATH_WRONG_PARAM_TYPE, "sqrt", "(focus)", base.fhirType(), "decimal or date");
}
@@ -4090,7 +4129,8 @@ public class FHIRPathEngine {
List result = new ArrayList();
if (focus.size() == 1) {
String cnt = focus.get(0).primitiveValue();
- for (String s : cnt.split(param)) {
+ String[] sl = Pattern.compile(param, Pattern.LITERAL).split(cnt);
+ for (String s : sl) {
result.add(new StringType(s));
}
}
@@ -4100,9 +4140,14 @@ public class FHIRPathEngine {
private List funcJoin(ExecutionContext context, List focus, ExpressionNode exp) {
List nl = execute(context, focus, exp.getParameters().get(0), true);
String param = nl.get(0).primitiveValue();
-
+ String param2 = param;
+ if (exp.getParameters().size() == 2) {
+ nl = execute(context, focus, exp.getParameters().get(1), true);
+ param2 = nl.get(0).primitiveValue();
+ }
+
List result = new ArrayList();
- CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(param);
+ CommaSeparatedStringBuilder b = new CommaSeparatedStringBuilder(param, param2);
for (Base i : focus) {
b.append(i.primitiveValue());
}
@@ -4164,6 +4209,38 @@ public class FHIRPathEngine {
return false;
}
+ private List funcComparable(ExecutionContext context, List focus, ExpressionNode exp) throws FHIRException {
+ if (focus.size() != 1 || !(focus.get(0).fhirType().equals("Quantity"))) {
+ return makeBoolean(false);
+ }
+ List nl = execute(context, focus, exp.getParameters().get(0), true);
+ if (nl.size() != 1 || !(nl.get(0).fhirType().equals("Quantity"))) {
+ return makeBoolean(false);
+ }
+ String s1 = getNamedValue(focus.get(0), "system");
+ String u1 = getNamedValue(focus.get(0), "code");
+ String s2 = getNamedValue(nl.get(0), "system");
+ String u2 = getNamedValue(nl.get(0), "code");
+
+ if (s1 == null || s2 == null || !s1.equals(s2)) {
+ return makeBoolean(false);
+ }
+ if (u1 == null || u2 == null) {
+ return makeBoolean(false);
+ }
+ if (u1.equals(u2)) {
+ return makeBoolean(true);
+ }
+ if (s1.equals("http://unitsofmeasure.org") && worker.getUcumService() != null) {
+ try {
+ return makeBoolean(worker.getUcumService().isComparable(u1, u2));
+ } catch (UcumException e) {
+ return makeBoolean(false);
+ }
+ } else {
+ return makeBoolean(false);
+ }
+ }
private boolean checkHtmlNames(XhtmlNode node) {
if (node.getNodeType() == NodeType.Comment) {
diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/GraphQLEngine.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/GraphQLEngine.java
index e288dacd6..758897ce2 100644
--- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/GraphQLEngine.java
+++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/GraphQLEngine.java
@@ -119,6 +119,13 @@ public class GraphQLEngine implements IGraphQLEngine {
default: return super.getNamedProperty(_hash, _name, _checkValid);
}
}
+
+
+ @Override
+ public Base copy() {
+ throw new Error("Not Implemented");
+ }
+
}
public static class SearchWrapper extends Base {
@@ -208,6 +215,12 @@ public class GraphQLEngine implements IGraphQLEngine {
return null;
}
+
+ @Override
+ public Base copy() {
+ throw new Error("Not Implemented");
+ }
+
}
private IWorkerContext context;
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/StructureDefinitionHacker.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/StructureDefinitionHacker.java
index 5d28c11c6..69e231200 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/StructureDefinitionHacker.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/StructureDefinitionHacker.java
@@ -26,8 +26,27 @@ public class StructureDefinitionHacker {
if (VersionUtilities.isR4Ver(version) && "http://hl7.org/fhir/StructureDefinition/example-composition".equals(sd.getUrl())) {
for (ElementDefinition ed : sd.getSnapshot().getElement()) {
fixDocSecURL(ed);
- } for (ElementDefinition ed : sd.getDifferential().getElement()) {
+ }
+ for (ElementDefinition ed : sd.getDifferential().getElement()) {
fixDocSecURL(ed);
+ if ("ClinicalImpression.problem".equals(ed.getPath())) {
+ // work around a bidi problem
+ ed.setComment("e.g. The patient is a pregnant, has congestive heart failure, has an Adenocarcinoma, and is allergic to penicillin.");
+ }
+ }
+ }
+ if (VersionUtilities.isR4Ver(version) && "http://hl7.org/fhir/StructureDefinition/ClinicalImpression".equals(sd.getUrl())) {
+ for (ElementDefinition ed : sd.getSnapshot().getElement()) {
+ if ("ClinicalImpression.problem".equals(ed.getPath())) {
+ // work around a bidi problem
+ ed.setComment("e.g. The patient is a pregnant, has congestive heart failure, has an Adenocarcinoma, and is allergic to penicillin.");
+ }
+ }
+ for (ElementDefinition ed : sd.getDifferential().getElement()) {
+ if ("ClinicalImpression.problem".equals(ed.getPath())) {
+ // work around a bidi problem
+ ed.setComment("e.g. The patient is a pregnant, has congestive heart failure, has an Adenocarcinoma, and is allergic to penicillin.");
+ }
}
}
return sd;
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java
index 1674d9eed..86baecc68 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java
@@ -2292,13 +2292,17 @@ public class ProfileUtilities extends TranslatingUtilities {
}
// Before applying changes, apply them to what's in the profile
StructureDefinition profile = null;
+ boolean msg = true;
if (base.hasSliceName()) {
profile = base.getType().size() == 1 && base.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, base.getTypeFirstRep().getProfile().get(0).getValue(), srcSD) : null;
}
if (profile==null) {
profile = source.getType().size() == 1 && source.getTypeFirstRep().hasProfile() ? context.fetchResource(StructureDefinition.class, source.getTypeFirstRep().getProfile().get(0).getValue(), derivedSrc) : null;
if (profile != null && !"Extension".equals(profile.getType()) && profile.getKind() != StructureDefinitionKind.RESOURCE && profile.getKind() != StructureDefinitionKind.LOGICAL) {
+ // this is a problem - we're kind of hacking things here. The problem is that we sometimes want the details from the profile to override the
+ // inherited attributes, and sometimes not
profile = null;
+ msg = false;
}
}
if (profile != null) {
@@ -2320,15 +2324,17 @@ public class ProfileUtilities extends TranslatingUtilities {
} else if (source.getType().size() == 1 && source.getTypeFirstRep().hasProfile() && !source.getTypeFirstRep().getProfile().get(0).hasExtension(ToolingExtensions.EXT_PROFILE_ELEMENT)) {
// todo: should we change down the profile_element if there's one?
String type = source.getTypeFirstRep().getWorkingCode();
- if ("Extension".equals(type)) {
- System.out.println("Can't find Extension definition for "+source.getTypeFirstRep().getProfile().get(0).asStringValue()+" but trying to go on");
- if (allowUnknownProfile != AllowUnknownProfile.ALL_TYPES) {
- throw new DefinitionException("Unable to find Extension definition for "+source.getTypeFirstRep().getProfile().get(0).asStringValue());
- }
- } else {
- System.out.println("Can't find "+type+" profile "+source.getTypeFirstRep().getProfile().get(0).asStringValue()+" but trying to go on");
- if (allowUnknownProfile == AllowUnknownProfile.NONE) {
- throw new DefinitionException("Unable to find "+type+" profile "+source.getTypeFirstRep().getProfile().get(0).asStringValue());
+ if (msg) {
+ if ("Extension".equals(type)) {
+ System.out.println("Can't find Extension definition for "+source.getTypeFirstRep().getProfile().get(0).asStringValue()+" but trying to go on");
+ if (allowUnknownProfile != AllowUnknownProfile.ALL_TYPES) {
+ throw new DefinitionException("Unable to find Extension definition for "+source.getTypeFirstRep().getProfile().get(0).asStringValue());
+ }
+ } else {
+ System.out.println("Can't find "+type+" profile "+source.getTypeFirstRep().getProfile().get(0).asStringValue()+" but trying to go on");
+ if (allowUnknownProfile == AllowUnknownProfile.NONE) {
+ throw new DefinitionException("Unable to find "+type+" profile "+source.getTypeFirstRep().getProfile().get(0).asStringValue());
+ }
}
}
}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BackboneElement.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BackboneElement.java
index 0046b3207..2ab1d0f7b 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BackboneElement.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/BackboneElement.java
@@ -35,6 +35,7 @@ import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import org.hl7.fhir.r5.model.Enumerations.*;
+import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.instance.model.api.IBaseDatatypeElement;
import org.hl7.fhir.exceptions.FHIRException;
import org.hl7.fhir.instance.model.api.ICompositeType;
@@ -304,6 +305,17 @@ Modifier extensions SHALL NOT change the meaning of any elements on Resource or
return java.util.Collections.unmodifiableList(retVal);
}
+ public void copyExtensions(org.hl7.fhir.r5.model.BackboneElement src, String... urls) {
+ super.copyExtensions(src,urls);
+ for (Extension e : src.getModifierExtension()) {
+ if (Utilities.existsInList(e.getUrl(), urls)) {
+ addModifierExtension(e.copy());
+ }
+ }
+ }
+
+
+
// end addition
}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Element.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Element.java
index 955a9cd90..1f5419a18 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Element.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Element.java
@@ -468,6 +468,15 @@ public abstract class Element extends Base implements IBaseHasExtensions, IBaseE
return false;
}
+ public void copyExtensions(org.hl7.fhir.r5.model.Element src, String... urls) {
+ for (Extension e : src.getExtension()) {
+ if (Utilities.existsInList(e.getUrl(), urls)) {
+ addExtension(e.copy());
+ }
+ }
+ }
+
+
// end addition
}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java
index 832327f08..ac42902f3 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java
@@ -1692,133 +1692,139 @@ public String toString() {
return ResourceType.Parameters;
}
-// Manual code (from Configuration.txt):
-public Parameters addParameter(String name, boolean b) {
- addParameter().setName(name).setValue(new BooleanType(b));
- return this;
-}
-
-public Parameters addParameter(String name, String s) {
- if (s != null)
- addParameter().setName(name).setValue(new StringType(s));
- return this;
-}
-
-public Parameters addParameter(String name, DataType v) {
- if (v != null)
- addParameter().setName(name).setValue(v);
- return this;
-}
-
-public Parameters setParameter(String name, boolean b) {
- for (ParametersParameterComponent p : getParameter()) {
- if (p.getName().equals(name)) {
- p.setValue(new BooleanType(b));
- return this;
- }
+ // Manual code (from Configuration.txt):
+ public Parameters addParameter(String name, boolean b) {
+ addParameter().setName(name).setValue(new BooleanType(b));
+ return this;
}
- addParameter().setName(name).setValue(new BooleanType(b));
- return this;
-}
-public Parameters setParameter(String name, String s) {
- if (s != null) {
+ public Parameters addParameter(String name, String s) {
+ if (s != null)
+ addParameter().setName(name).setValue(new StringType(s));
+ return this;
+ }
+
+ public Parameters addParameter(String name, DataType v) {
+ if (v != null)
+ addParameter().setName(name).setValue(v);
+ return this;
+ }
+
+ public Parameters setParameter(String name, boolean b) {
for (ParametersParameterComponent p : getParameter()) {
if (p.getName().equals(name)) {
- p.setValue(new StringType(s));
+ p.setValue(new BooleanType(b));
return this;
}
}
- addParameter().setName(name).setValue(new StringType(s));
+ addParameter().setName(name).setValue(new BooleanType(b));
+ return this;
}
- return this;
-}
-public Parameters setParameter(String name, DataType v) {
- if (v != null) {
- for (ParametersParameterComponent p : getParameter() ) {
+ public Parameters setParameter(String name, String s) {
+ if (s != null) {
+ for (ParametersParameterComponent p : getParameter()) {
+ if (p.getName().equals(name)) {
+ p.setValue(new StringType(s));
+ return this;
+ }
+ }
+ addParameter().setName(name).setValue(new StringType(s));
+ }
+ return this;
+ }
+
+ public Parameters setParameter(String name, DataType v) {
+ if (v != null) {
+ for (ParametersParameterComponent p : getParameter() ) {
+ if (p.getName().equals(name)) {
+ p.setValue(v);
+ return this;
+ }
+ }
+ addParameter().setName(name).setValue(v);
+ }
+ return this;
+ }
+
+ public boolean hasParameterValue(String name) {
+ for (ParametersParameterComponent p : getParameter()) {
+ if (p.getName().equals(name) && p.hasValue())
+ return true;
+ }
+ return false;
+ }
+
+ public boolean hasParameter(String name) {
+ for (ParametersParameterComponent p : getParameter()) {
+ if (p.getName().equals(name))
+ return true;
+ }
+ return false;
+ }
+
+ public DataType getParameterValue(String name) {
+ for (ParametersParameterComponent p : getParameter()) {
+ if (p.getName().equals(name))
+ return p.getValue();
+ }
+ return null;
+ }
+
+ public ParametersParameterComponent getParameter(String name) {
+ for (ParametersParameterComponent p : getParameter()) {
+ if (p.getName().equals(name))
+ return p;
+ }
+ return null;
+ }
+
+ public List getParameterValues(String name) {
+ List res = new ArrayList<>();
+ for (ParametersParameterComponent p : getParameter()) {
+ if (p.getName().equals(name))
+ res.add(p.getValue());
+ }
+ return res;
+ }
+
+ public List getParameters(String name) {
+ List res = new ArrayList();
+ for (ParametersParameterComponent p : getParameter()) {
+ if (p.getName().equals(name))
+ res.add(p);
+ }
+ return res;
+ }
+
+ public boolean getParameterBool(String name) {
+ for (ParametersParameterComponent p : getParameter()) {
if (p.getName().equals(name)) {
- p.setValue(v);
- return this;
+ if (p.getValue() instanceof BooleanType)
+ return ((BooleanType) p.getValue()).booleanValue();
+ boolean ok = Boolean.getBoolean(p.getValue().primitiveValue());
+ return ok;
}
}
- addParameter().setName(name).setValue(v);
+ return false;
}
- return this;
-}
-public boolean hasParameterValue(String name) {
- for (ParametersParameterComponent p : getParameter()) {
- if (p.getName().equals(name) && p.hasValue())
- return true;
- }
- return false;
-}
-public boolean hasParameter(String name) {
- for (ParametersParameterComponent p : getParameter()) {
- if (p.getName().equals(name))
- return true;
- }
- return false;
-}
-
-public DataType getParameterValue(String name) {
- for (ParametersParameterComponent p : getParameter()) {
- if (p.getName().equals(name))
- return p.getValue();
- }
- return null;
-}
-
-public ParametersParameterComponent getParameter(String name) {
- for (ParametersParameterComponent p : getParameter()) {
- if (p.getName().equals(name))
- return p;
- }
- return null;
-}
-
-public List getParameterValues(String name) {
- List res = new ArrayList<>();
- for (ParametersParameterComponent p : getParameter()) {
- if (p.getName().equals(name))
- res.add(p.getValue());
- }
- return res;
-}
-
-public List getParameters(String name) {
- List res = new ArrayList();
- for (ParametersParameterComponent p : getParameter()) {
- if (p.getName().equals(name))
- res.add(p);
- }
- return res;
-}
-
-public boolean getParameterBool(String name) {
- for (ParametersParameterComponent p : getParameter()) {
- if (p.getName().equals(name)) {
- if (p.getValue() instanceof BooleanType)
- return ((BooleanType) p.getValue()).booleanValue();
- boolean ok = Boolean.getBoolean(p.getValue().primitiveValue());
- return ok;
+ public String getParameterString(String name) {
+ for (ParametersParameterComponent p : getParameter()) {
+ if (p.getName().equals(name)) {
+ if (p.getValue() instanceof PrimitiveType)
+ return ((PrimitiveType) p.getValue()).primitiveValue();
+ }
}
+ return null;
}
- return false;
-}
-// end addition
-public String getParameterString(String name) {
- for (ParametersParameterComponent p : getParameter()) {
- if (p.getName().equals(name)) {
- if (p.getValue() instanceof PrimitiveType)
- return ((PrimitiveType) p.getValue()).primitiveValue();
- }
+ public void clearParameters(String name) {
+ getParameter().removeIf(p -> name.equals(p.getName()));
}
- return null;
-}
+
+ // end addition
}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ObligationsRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ObligationsRenderer.java
index 795b7cb29..b7aaf40f6 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ObligationsRenderer.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ObligationsRenderer.java
@@ -66,6 +66,9 @@ public class ObligationsRenderer {
for (Extension usage : ext.getExtensionsByUrl("usage")) {
this.usage.add(usage.getValueUsageContext());
}
+ for (Extension eid : ext.getExtensionsByUrl("elementId")) {
+ this.elementIds.add(eid.getValue().primitiveValue());
+ }
this.isUnchanged = ext.hasUserData(ProfileUtilities.UD_DERIVATION_EQUALS);
}
@@ -165,6 +168,10 @@ public class ObligationsRenderer {
seeObligations(list, null, false, "$all");
}
+ public void seeRootObligations(String eid, List list) {
+ seeRootObligations(eid, list, null, false, "$all");
+ }
+
public void seeObligations(List list, List compList, boolean compare, String id) {
HashMap compBindings = new HashMap();
if (compare && compList!=null) {
@@ -179,7 +186,7 @@ public class ObligationsRenderer {
for (Extension ext : list) {
ObligationDetail obd = obligationDetail(ext);
- if (("$all".equals(id) && !obd.hasActor()) || (obd.hasActor(id))) {
+ if ("$all".equals(id) || (obd.hasActor(id))) {
if (compare && compList!=null) {
ObligationDetail match = null;
do {
@@ -192,8 +199,9 @@ public class ObligationsRenderer {
obligations.add(obd);
if (obd.compare!=null)
compBindings.remove(obd.compare.getKey());
- } else
+ } else {
obligations.add(obd);
+ }
}
}
for (ObligationDetail b: compBindings.values()) {
@@ -202,6 +210,61 @@ public class ObligationsRenderer {
}
}
+ public void seeRootObligations(String eid, List list, List compList, boolean compare, String id) {
+ HashMap compBindings = new HashMap();
+ if (compare && compList!=null) {
+ for (Extension ext : compList) {
+ if (forElement(eid, ext)) {
+ ObligationDetail abr = obligationDetail(ext);
+ if (compBindings.containsKey(abr.getKey())) {
+ abr.incrementCount();
+ }
+ compBindings.put(abr.getKey(), abr);
+ }
+ }
+ }
+
+ for (Extension ext : list) {
+ if (forElement(eid, ext)) {
+ ObligationDetail obd = obligationDetail(ext);
+ obd.elementIds.clear();
+ if ("$all".equals(id) || (obd.hasActor(id))) {
+ if (compare && compList!=null) {
+ ObligationDetail match = null;
+ do {
+ match = compBindings.get(obd.getKey());
+ if (obd.alreadyMatched())
+ obd.incrementCount();
+ } while (match!=null && obd.alreadyMatched());
+ if (match!=null)
+ obd.setCompare(match);
+ obligations.add(obd);
+ if (obd.compare!=null)
+ compBindings.remove(obd.compare.getKey());
+ } else {
+ obligations.add(obd);
+ }
+ }
+ }
+ }
+ for (ObligationDetail b: compBindings.values()) {
+ b.removed = true;
+ obligations.add(b);
+ }
+ }
+
+
+ private boolean forElement(String eid, Extension ext) {
+
+ for (Extension exid : ext.getExtensionsByUrl("elementId")) {
+ if (eid.equals(exid.getValue().primitiveValue())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
protected ObligationDetail obligationDetail(Extension ext) {
ObligationDetail abr = new ObligationDetail(ext);
return abr;
@@ -273,11 +336,13 @@ public class ObligationsRenderer {
boolean usage = false;
boolean actor = false;
boolean filter = false;
+ boolean elementId = false;
for (ObligationDetail binding : obligations) {
actor = actor || binding.actorId!=null || (binding.compare!=null && binding.compare.actorId !=null);
doco = doco || binding.getDoco(fullDoco)!=null || (binding.compare!=null && binding.compare.getDoco(fullDoco)!=null);
usage = usage || !binding.usage.isEmpty() || (binding.compare!=null && !binding.compare.usage.isEmpty());
filter = filter || binding.filter != null || (binding.compare!=null && binding.compare.filter!=null);
+ elementId = elementId || !binding.elementIds.isEmpty() || (binding.compare!=null && !binding.compare.elementIds.isEmpty());
}
XhtmlNode tr = new XhtmlNode(NodeType.Element, "tr");
@@ -286,6 +351,9 @@ public class ObligationsRenderer {
if (actor) {
tr.td().style("font-size: 11px").tx("Actor");
}
+ if (elementId) {
+ tr.td().style("font-size: 11px").tx("Elements");
+ }
if (usage) {
tr.td().style("font-size: 11px").tx("Usage");
}
@@ -342,6 +410,29 @@ public class ObligationsRenderer {
}
}
}
+ if (elementId) {
+ XhtmlNode elementIds = tr.td().style("font-size: 11px");
+ if (ob.compare!=null && ob.elementIds.equals(ob.compare.elementIds))
+ elementIds.style(STYLE_UNCHANGED);
+ for (String eid : ob.elementIds) {
+ elementIds.sep(", ");
+ ElementDefinition ed = profile.getSnapshot().getElementById(eid);
+ if (ed != null) {
+ elementIds.ah("#"+eid).tx(ed.getName());
+ } else {
+ elementIds.code().tx(eid);
+ }
+ }
+
+ if (ob.compare!=null && !ob.compare.elementIds.isEmpty()) {
+ for (String eid : ob.compare.elementIds) {
+ if (!ob.elementIds.contains(eid)) {
+ elementIds.sep(", ");
+ elementIds.span(STYLE_REMOVED, null).code().tx(eid);
+ }
+ }
+ }
+ }
if (usage) {
if (ob.usage != null) {
boolean first = true;
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java
index 5f0ff0cc2..8d00f8240 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java
@@ -837,8 +837,14 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
if (element != null && element.getIsModifier()) {
checkForNoChange(element.getIsModifierElement(), gc.addStyledText(translate("sd.table", "This element is a modifier element"), "?!", null, null, null, false));
}
- if (element != null && element.getMustSupport()) {
- checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element must be supported"), "S", "white", "red", null, false));
+ if (element != null) {
+ if (element.getMustSupport() && element.hasExtension(ToolingExtensions.EXT_OBLIGATION)) {
+ checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element has obligations and must be supported"), "SO", "white", "red", null, false));
+ } else if (element.getMustSupport()) {
+ checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element must be supported"), "S", "white", "red", null, false));
+ } else if (element != null && element.hasExtension(ToolingExtensions.EXT_OBLIGATION)) {
+ checkForNoChange(element.getMustSupportElement(), gc.addStyledText(translate("sd.table", "This element has obligations"), "O", "white", "red", null, false));
+ }
}
if (element != null && element.getIsSummary()) {
checkForNoChange(element.getIsSummaryElement(), gc.addStyledText(translate("sd.table", "This element is included in summaries"), "\u03A3", null, null, null, false));
@@ -1381,6 +1387,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer {
if (definition.hasExtension(ToolingExtensions.EXT_OBLIGATION)) {
obr.seeObligations(definition.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION));
}
+ if (!definition.getPath().contains(".") && profile.hasExtension(ToolingExtensions.EXT_OBLIGATION)) {
+ obr.seeObligations(profile.getExtensionsByUrl(ToolingExtensions.EXT_OBLIGATION));
+ }
obr.renderTable(gen, c);
if (definition.hasMaxLength() && definition.getMaxLength()!=0) {
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java
index 429d678d8..687e385bb 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/CodeSystemUtilities.java
@@ -466,6 +466,31 @@ public class CodeSystemUtilities {
return null;
}
+ public static ConceptDefinitionComponent findCodeOrAltCode(List list, String code, String use) {
+ for (ConceptDefinitionComponent c : list) {
+ if (c.getCode().equals(code))
+ return c;
+ for (ConceptPropertyComponent p : c.getProperty()) {
+ if ("alternateCode".equals(p.getCode()) && (use == null || hasUse(p, use)) && p.hasValue() && p.getValue().isPrimitive() && code.equals(p.getValue().primitiveValue())) {
+ return c;
+ }
+ }
+ ConceptDefinitionComponent s = findCodeOrAltCode(c.getConcept(), code, use);
+ if (s != null)
+ return s;
+ }
+ return null;
+ }
+
+ private static boolean hasUse(ConceptPropertyComponent p, String use) {
+ for (Extension ext : p.getExtensionsByUrl(ToolingExtensions.EXT_CS_ALTERNATE_USE)) {
+ if (ext.hasValueCoding() && use.equals(ext.getValueCoding().getCode())) {
+ return true;
+ }
+ }
+ return false;
+ }
+
public static void markStatus(CodeSystem cs, String wg, StandardsStatus status, String pckage, String fmm, String normativeVersion) throws FHIRException {
if (wg != null) {
if (!ToolingExtensions.hasExtension(cs, ToolingExtensions.EXT_WORKGROUP) ||
@@ -755,7 +780,7 @@ public class CodeSystemUtilities {
} else {
code = defineProperty(ret, p.getCode(), propertyTypeForType(p.getValue()));
}
- fdef.addProperty().setCode(code).setValue(p.getValue());
+ fdef.addProperty().setCode(code).setValue(p.getValue()).copyExtensions(p, "http://hl7.org/fhir/StructureDefinition/alternate-code-use", "http://hl7.org/fhir/StructureDefinition/alternate-code-status");
}
}
for (ConceptDefinitionComponent t : fdef.getConcept()) {
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java
index 5cfacb7be..c998a4074 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/expansion/ValueSetExpander.java
@@ -114,14 +114,15 @@ import org.hl7.fhir.r5.terminologies.ValueSetUtilities;
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProvider;
import org.hl7.fhir.r5.terminologies.providers.CodeSystemProviderExtension;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
+import org.hl7.fhir.r5.terminologies.utilities.ValueSetProcessBase;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.i18n.I18nConstants;
-public class ValueSetExpander {
+public class ValueSetExpander extends ValueSetProcessBase {
- private static final boolean REPORT_VERSION_ANYWAY = false;
+ private static final boolean REPORT_VERSION_ANYWAY = true;
private IWorkerContext context;
private ValueSet focus;
@@ -247,14 +248,14 @@ public class ValueSetExpander {
if (csProps != null && p.hasValue()) {
for (ConceptPropertyComponent cp : csProps) {
if (p.getValue().primitiveValue().equals(cp.getCode())) {
- n.addProperty().setCode(cp.getCode()).setValue(cp.getValue());
+ n.addProperty().setCode(cp.getCode()).setValue(cp.getValue()).copyExtensions(cp, "http://hl7.org/fhir/StructureDefinition/alternate-code-use", "http://hl7.org/fhir/StructureDefinition/alternate-code-status");
}
}
}
if (expProps != null && p.hasValue()) {
for (org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent cp : expProps) {
if (p.getValue().primitiveValue().equals(cp.getCode())) {
- n.addProperty(cp);
+ n.addProperty(cp).copyExtensions(cp, "http://hl7.org/fhir/StructureDefinition/alternate-code-use", "http://hl7.org/fhir/StructureDefinition/alternate-code-status");
}
}
}
@@ -313,12 +314,29 @@ public class ValueSetExpander {
private void addCodeAndDescendents(WorkingContext wc, ValueSetExpansionContainsComponent focus, ValueSetExpansionContainsComponent parent, Parameters expParams, List filters, boolean noInactive, List vsProps, ValueSet vsSrc) throws FHIRException, ETooCostly {
focus.checkNoModifiers("Expansion.contains", "expanding");
- ValueSetExpansionContainsComponent np = addCode(wc, focus.getSystem(), focus.getCode(), focus.getDisplay(), vsSrc.getLanguage(), parent,
- convert(focus.getDesignation()), expParams, focus.getAbstract(), focus.getInactive(), focus.getExtensionString(ToolingExtensions.EXT_DEFINITION), filters, noInactive, false, vsProps, null, focus.getProperty(), null, focus.getExtension());
+ ValueSetExpansionContainsComponent np = null;
+ for (String code : getCodesForConcept(focus, expParams)) {
+ ValueSetExpansionContainsComponent t = addCode(wc, focus.getSystem(), code, focus.getDisplay(), vsSrc.getLanguage(), parent,
+ convert(focus.getDesignation()), expParams, focus.getAbstract(), focus.getInactive(), focus.getExtensionString(ToolingExtensions.EXT_DEFINITION), filters, noInactive, false, vsProps, null, focus.getProperty(), null, focus.getExtension());
+ if (np == null) {
+ np = t;
+ }
+ }
for (ValueSetExpansionContainsComponent c : focus.getContains())
addCodeAndDescendents(wc, c, np, expParams, filters, noInactive, vsProps, vsSrc);
}
+ private List getCodesForConcept(ValueSetExpansionContainsComponent focus, Parameters expParams) {
+ List codes = new ArrayList<>();
+ codes.add(focus.getCode());
+ for (org.hl7.fhir.r5.model.ValueSet.ConceptPropertyComponent p : focus.getProperty()) {
+ if ("alternateCode".equals(p.getCode()) && (altCodeParams.passes(p.getExtension())) && p.getValue().isPrimitive()) {
+ codes.add(p.getValue().primitiveValue());
+ }
+ }
+ return codes;
+ }
+
private List convert(List designations) {
List list = new ArrayList();
for (ConceptReferenceDesignationComponent d : designations) {
@@ -343,7 +361,12 @@ public class ValueSetExpander {
boolean inc = CodeSystemUtilities.isInactive(cs, def);
boolean dep = CodeSystemUtilities.isDeprecated(cs, def, false);
if ((includeAbstract || !abs) && filterFunc.includeConcept(cs, def) && passesOtherFilters(otherFilters, cs, def.getCode())) {
- np = addCode(wc, system, def.getCode(), def.getDisplay(), cs.getLanguage(), parent, def.getDesignation(), expParams, abs, inc, def.getDefinition(), filters, noInactive, dep, vsProps, def.getProperty(), null, def.getExtension(), null);
+ for (String code : getCodesForConcept(def, expParams)) {
+ ValueSetExpansionContainsComponent t = addCode(wc, system, code, def.getDisplay(), cs.getLanguage(), parent, def.getDesignation(), expParams, abs, inc, def.getDefinition(), filters, noInactive, dep, vsProps, def.getProperty(), null, def.getExtension(), null);
+ if (np == null) {
+ np = t;
+ }
+ }
}
for (ConceptDefinitionComponent c : def.getConcept()) {
addCodeAndDescendents(wc, cs, system, c, np, expParams, filters, exclusion, filterFunc, noInactive, vsProps, otherFilters);
@@ -353,10 +376,31 @@ public class ValueSetExpander {
for (ConceptDefinitionComponent c : children)
addCodeAndDescendents(wc, cs, system, c, np, expParams, filters, exclusion, filterFunc, noInactive, vsProps, otherFilters);
}
-
}
+ private List getCodesForConcept(ConceptDefinitionComponent focus, Parameters expParams) {
+ List codes = new ArrayList<>();
+ codes.add(focus.getCode());
+ for (ConceptPropertyComponent p : focus.getProperty()) {
+ if ("alternateCode".equals(p.getCode()) && (altCodeParams.passes(p.getExtension())) && p.getValue().isPrimitive()) {
+ codes.add(p.getValue().primitiveValue());
+ }
+ }
+ return codes;
+ }
+
+ private static boolean hasUse(ConceptPropertyComponent p, List uses) {
+ for (Extension ext : p.getExtensionsByUrl(ToolingExtensions.EXT_CS_ALTERNATE_USE)) {
+ if (ext.hasValueCoding() && Utilities.existsInList(ext.getValueCoding().getCode(), uses)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+
+
private void addCodes(ValueSetExpansionComponent expand, List params, Parameters expParams, List filters, boolean noInactive, List vsProps, ValueSet vsSrc) throws ETooCostly, FHIRException {
if (expand != null) {
if (expand.getContains().size() > maxExpansionSize)
@@ -450,6 +494,9 @@ public class ValueSetExpander {
public ValueSetExpansionOutcome doExpand(ValueSet source, Parameters expParams) throws FHIRException, ETooCostly, FileNotFoundException, IOException, CodeSystemProviderExtension {
if (expParams == null)
expParams = makeDefaultExpansion();
+ altCodeParams.seeParameters(expParams);
+ altCodeParams.seeValueSet(source);
+
source.checkNoModifiers("ValueSet", "expanding");
focus = source.copy();
focus.setIdBase(null);
@@ -755,6 +802,13 @@ public class ValueSetExpander {
UriType u = new UriType(cs.getUrl() + (cs.hasVersion() ? "|"+cs.getVersion() : ""));
if (!existsInParams(exp.getParameter(), "version", u))
exp.getParameter().add(new ValueSetExpansionParameterComponent().setName("version").setValue(u));
+ if (cs.hasUserData("supplements.installed")) {
+ for (String s : cs.getUserString("supplements.installed").split("\\,")) {
+ u = new UriType(s);
+ if (!existsInParams(exp.getParameter(), "version", u))
+ exp.getParameter().add(new ValueSetExpansionParameterComponent().setName("version").setValue(u));
+ }
+ }
}
if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) {
// special case - add all the code system
@@ -773,11 +827,10 @@ public class ValueSetExpander {
dwc.setCanBeHeirarchy(false);
for (ConceptReferenceComponent c : inc.getConcept()) {
c.checkNoModifiers("Code in Value Set", "expanding");
- ConceptDefinitionComponent def = CodeSystemUtilities.findCode(cs.getConcept(), c.getCode());
+ ConceptDefinitionComponent def = CodeSystemUtilities.findCodeOrAltCode(cs.getConcept(), c.getCode(), null);
boolean inactive = false; // default is true if we're a fragment and
boolean isAbstract = false;
if (def == null) {
- def.checkNoModifiers("Code in Code System", "expanding");
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
addFragmentWarning(exp, cs);
} else if (cs.getContent() == CodeSystemContentMode.EXAMPLE) {
@@ -788,6 +841,7 @@ public class ValueSetExpander {
}
}
} else {
+ def.checkNoModifiers("Code in Code System", "expanding");
inactive = CodeSystemUtilities.isInactive(cs, def);
isAbstract = CodeSystemUtilities.isNotSelectable(cs, def);
}
@@ -845,14 +899,16 @@ public class ValueSetExpander {
}
} else if ("display".equals(fc.getProperty()) && fc.getOp() == FilterOperator.EQUAL) {
- // gg; note: wtf is this: if the filter is display=v, look up the code 'v', and see if it's diplsay is 'v'?
+ // gg; note: wtf is this: if the filter is display=v, look up the code 'v', and see if it's display is 'v'?
dwc.setCanBeHeirarchy(false);
ConceptDefinitionComponent def = getConceptForCode(cs.getConcept(), fc.getValue());
if (def != null) {
if (isNotBlank(def.getDisplay()) && isNotBlank(fc.getValue())) {
if (def.getDisplay().contains(fc.getValue()) && passesOtherFilters(filters, cs, def.getCode())) {
- addCode(wc, inc.getSystem(), def.getCode(), def.getDisplay(), cs.getLanguage(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
- def.getDefinition(), imports, noInactive, false, exp.getProperty(), def.getProperty(), null, def.getExtension(), null);
+ for (String code : getCodesForConcept(def, expParams)) {
+ ValueSetExpansionContainsComponent t = addCode(wc, inc.getSystem(), code, def.getDisplay(), cs.getLanguage(), null, def.getDesignation(), expParams, CodeSystemUtilities.isNotSelectable(cs, def), CodeSystemUtilities.isInactive(cs, def),
+ def.getDefinition(), imports, noInactive, false, exp.getProperty(), def.getProperty(), null, def.getExtension(), null);
+ }
}
}
}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java
new file mode 100644
index 000000000..30de48170
--- /dev/null
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/utilities/ValueSetProcessBase.java
@@ -0,0 +1,82 @@
+package org.hl7.fhir.r5.terminologies.utilities;
+
+import java.util.*;
+
+import org.hl7.fhir.r5.model.Extension;
+import org.hl7.fhir.r5.model.Parameters;
+import org.hl7.fhir.r5.model.ValueSet;
+import org.hl7.fhir.r5.model.BooleanType;
+import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
+import org.hl7.fhir.r5.model.DataType;
+import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
+import org.hl7.fhir.r5.model.PrimitiveType;
+import org.hl7.fhir.r5.utils.ToolingExtensions;
+import org.hl7.fhir.utilities.Utilities;
+
+public class ValueSetProcessBase {
+
+ public static class AlternateCodesProcessingRules {
+ private boolean all;
+ private List uses = new ArrayList<>();
+
+ public AlternateCodesProcessingRules(boolean b) {
+ all = b;
+ }
+
+ private void seeParameter(DataType value) {
+ if (value != null) {
+ if (value instanceof BooleanType) {
+ all = ((BooleanType) value).booleanValue();
+ uses.clear();
+ } else if (value.isPrimitive()) {
+ String s = value.primitiveValue();
+ if (!Utilities.noString(s)) {
+ uses.add(s);
+ }
+ }
+ }
+ }
+
+ public void seeParameters(Parameters pp) {
+ for (ParametersParameterComponent p : pp.getParameter()) {
+ String name = p.getName();
+ if ("includeAlternateCodes".equals(name)) {
+ DataType value = p.getValue();
+ seeParameter(value);
+ }
+ }
+ }
+
+ public void seeValueSet(ValueSet vs) {
+ if (vs != null) {
+ for (Extension ext : vs.getCompose().getExtension()) {
+ if ("http://hl7.org/fhir/tools/StructureDefinion/valueset-expansion-param".equals(ext.getUrl())) {
+ String name = ext.getExtensionString("name");
+ Extension value = ext.getExtensionByUrl("value");
+ if ("includeAlternateCodes".equals(name) && value != null && value.hasValue()) {
+ seeParameter(value.getValue());
+ }
+ }
+ }
+ }
+ }
+
+ public boolean passes(List extensions) {
+ if (all) {
+ return true;
+ }
+
+ for (Extension ext : extensions) {
+ if (ToolingExtensions.EXT_CS_ALTERNATE_USE.equals(ext.getUrl())) {
+ if (ext.hasValueCoding() && Utilities.existsInList(ext.getValueCoding().getCode(), uses)) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ }
+
+ protected AlternateCodesProcessingRules altCodeParams = new AlternateCodesProcessingRules(false);
+ protected AlternateCodesProcessingRules allAltCodes = new AlternateCodesProcessingRules(true);
+}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java
index 68b17303f..f27b37653 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/validation/ValueSetValidator.java
@@ -53,12 +53,15 @@ import org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode;
import org.hl7.fhir.r5.model.Enumerations.FilterOperator;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent;
import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent;
+import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent;
import org.hl7.fhir.r5.model.CodeableConcept;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.DataType;
+import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Enumerations.PublicationStatus;
import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
+import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.PackageInformation;
import org.hl7.fhir.r5.model.Parameters;
import org.hl7.fhir.r5.model.TerminologyCapabilities.TerminologyCapabilitiesCodeSystemComponent;
@@ -77,6 +80,7 @@ import org.hl7.fhir.r5.terminologies.providers.CodeSystemProvider;
import org.hl7.fhir.r5.terminologies.providers.SpecialCodeSystem;
import org.hl7.fhir.r5.terminologies.providers.URICodeSystem;
import org.hl7.fhir.r5.terminologies.utilities.TerminologyServiceErrorClass;
+import org.hl7.fhir.r5.terminologies.utilities.ValueSetProcessBase;
import org.hl7.fhir.r5.utils.ToolingExtensions;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier;
import org.hl7.fhir.r5.utils.validation.ValidationContextCarrier.ValidationContextResourceProxy;
@@ -90,7 +94,7 @@ import org.hl7.fhir.utilities.validation.ValidationOptions.ValueSetMode;
import com.google.j2objc.annotations.ReflectionSupport.Level;
-public class ValueSetValidator {
+public class ValueSetValidator extends ValueSetProcessBase {
private ValueSet valueset;
private IWorkerContext context;
@@ -98,7 +102,7 @@ public class ValueSetValidator {
private ValidationOptions options;
private ValidationContextCarrier localContext;
private List localSystems = new ArrayList<>();
- Parameters expansionProfile;
+ protected Parameters expansionProfile;
private TerminologyCapabilities txCaps;
private Set unknownSystems;
private boolean throwToServer;
@@ -109,6 +113,7 @@ public class ValueSetValidator {
this.options = options;
this.expansionProfile = expansionProfile;
this.txCaps = txCaps;
+ analyseValueSet();
}
public ValueSetValidator(ValidationOptions options, ValueSet source, IWorkerContext context, ValidationContextCarrier ctxt, Parameters expansionProfile, TerminologyCapabilities txCaps) {
@@ -139,6 +144,8 @@ public class ValueSetValidator {
}
private void analyseValueSet() {
+ altCodeParams.seeParameters(expansionProfile);
+ altCodeParams.seeValueSet(valueset);
if (localContext != null) {
if (valueset != null) {
for (ConceptSetComponent i : valueset.getCompose().getInclude()) {
@@ -295,7 +302,7 @@ public class ValueSetValidator {
private String lookupDisplay(Coding c) {
CodeSystem cs = resolveCodeSystem(c.getSystem(), c.getVersion());
if (cs != null) {
- ConceptDefinitionComponent cd = CodeSystemUtilities.getCode(cs, c.getCode());
+ ConceptDefinitionComponent cd = CodeSystemUtilities.findCodeOrAltCode(cs.getConcept(), c.getCode(), null);
if (cd != null) {
return getPreferredDisplay(cd, cs);
}
@@ -648,7 +655,7 @@ public class ValueSetValidator {
}
private ValidationResult validateCode(String path, Coding code, CodeSystem cs, CodeableConcept vcc) {
- ConceptDefinitionComponent cc = cs.hasUserData("tx.cs.special") ? ((SpecialCodeSystem) cs.getUserData("tx.cs.special")).findConcept(code) : findCodeInConcept(cs.getConcept(), code.getCode());
+ ConceptDefinitionComponent cc = cs.hasUserData("tx.cs.special") ? ((SpecialCodeSystem) cs.getUserData("tx.cs.special")).findConcept(code) : findCodeInConcept(cs.getConcept(), code.getCode(), allAltCodes);
if (cc == null) {
if (cs.getContent() == CodeSystemContentMode.FRAGMENT) {
String msg = context.formatMessage(I18nConstants.UNKNOWN_CODE__IN_FRAGMENT, code.getCode(), cs.getUrl());
@@ -658,7 +665,7 @@ public class ValueSetValidator {
return new ValidationResult(IssueSeverity.ERROR, msg, makeIssue(IssueSeverity.ERROR, IssueType.INVALID, path+".code", msg));
}
}
- Coding vc = new Coding().setCode(code.getCode()).setSystem(cs.getUrl()).setVersion(cs.getVersion()).setDisplay(getPreferredDisplay(cc, cs));
+ Coding vc = new Coding().setCode(cc.getCode()).setSystem(cs.getUrl()).setVersion(cs.getVersion()).setDisplay(getPreferredDisplay(cc, cs));
if (vcc != null) {
vcc.addCoding(vc);
}
@@ -812,18 +819,18 @@ public class ValueSetValidator {
return true;
}
- private ConceptDefinitionComponent findCodeInConcept(ConceptDefinitionComponent concept, String code) {
+ private ConceptDefinitionComponent findCodeInConcept(ConceptDefinitionComponent concept, String code, AlternateCodesProcessingRules altCodeRules) {
if (code.equals(concept.getCode())) {
return concept;
}
- ConceptDefinitionComponent cc = findCodeInConcept(concept.getConcept(), code);
+ ConceptDefinitionComponent cc = findCodeInConcept(concept.getConcept(), code, altCodeRules);
if (cc != null) {
return cc;
}
if (concept.hasUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK)) {
List children = (List) concept.getUserData(CodeSystemUtilities.USER_DATA_CROSS_LINK);
for (ConceptDefinitionComponent c : children) {
- cc = findCodeInConcept(c, code);
+ cc = findCodeInConcept(c, code, altCodeRules);
if (cc != null) {
return cc;
}
@@ -832,12 +839,15 @@ public class ValueSetValidator {
return null;
}
- private ConceptDefinitionComponent findCodeInConcept(List concept, String code) {
+ private ConceptDefinitionComponent findCodeInConcept(List concept, String code, AlternateCodesProcessingRules altCodeRules) {
for (ConceptDefinitionComponent cc : concept) {
if (code.equals(cc.getCode())) {
return cc;
}
- ConceptDefinitionComponent c = findCodeInConcept(cc, code);
+ if (Utilities.existsInList(code, alternateCodes(cc, altCodeRules))) {
+ return cc;
+ }
+ ConceptDefinitionComponent c = findCodeInConcept(cc, code, altCodeRules);
if (c != null) {
return c;
}
@@ -846,6 +856,17 @@ public class ValueSetValidator {
}
+ private List alternateCodes(ConceptDefinitionComponent focus, AlternateCodesProcessingRules altCodeRules) {
+ List codes = new ArrayList<>();
+ for (ConceptPropertyComponent p : focus.getProperty()) {
+ if ("alternateCode".equals(p.getCode()) && (altCodeRules.passes(p.getExtension())) && p.getValue().isPrimitive()) {
+ codes.add(p.getValue().primitiveValue());
+ }
+ }
+ return codes;
+ }
+
+
private String systemForCodeInValueSet(String code, List problems) {
Set sys = new HashSet<>();
if (!scanForCodeInValueSet(code, sys, problems)) {
@@ -904,7 +925,7 @@ public class ValueSetValidator {
}
}
} else {
- ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), code);
+ ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), code, allAltCodes);
if (cc != null) {
sys.add(vsi.getSystem());
}
@@ -1086,7 +1107,7 @@ public class ValueSetValidator {
}
List list = cs.getConcept();
- ok = validateCodeInConceptList(code, cs, list);
+ ok = validateCodeInConceptList(code, cs, list, allAltCodes);
if (ok && vsi.hasConcept()) {
for (ConceptReferenceComponent cc : vsi.getConcept()) {
if (cc.getCode().equals(code)) {
@@ -1095,6 +1116,8 @@ public class ValueSetValidator {
}
return false;
} else {
+ // recheck that this is a valid alternate code
+ ok = validateCodeInConceptList(code, cs, list, altCodeParams);
return ok;
}
}
@@ -1163,21 +1186,24 @@ public class ValueSetValidator {
if (!excludeRoot && code.equals(f.getValue())) {
return true;
}
- ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), f.getValue());
+ ConceptDefinitionComponent cc = findCodeInConcept(cs.getConcept(), f.getValue(), altCodeParams);
if (cc == null) {
return false;
}
- ConceptDefinitionComponent cc2 = findCodeInConcept(cc, code);
+ ConceptDefinitionComponent cc2 = findCodeInConcept(cc, code, altCodeParams);
return cc2 != null && cc2 != cc;
}
- public boolean validateCodeInConceptList(String code, CodeSystem def, List list) {
+ public boolean validateCodeInConceptList(String code, CodeSystem def, List list, AlternateCodesProcessingRules altCodeRules) {
if (def.getCaseSensitive()) {
for (ConceptDefinitionComponent cc : list) {
if (cc.getCode().equals(code)) {
return true;
}
- if (cc.hasConcept() && validateCodeInConceptList(code, def, cc.getConcept())) {
+ if (Utilities.existsInList(code, alternateCodes(cc, altCodeRules))) {
+ return true;
+ }
+ if (cc.hasConcept() && validateCodeInConceptList(code, def, cc.getConcept(), altCodeRules)) {
return true;
}
}
@@ -1186,7 +1212,7 @@ public class ValueSetValidator {
if (cc.getCode().equalsIgnoreCase(code)) {
return true;
}
- if (cc.hasConcept() && validateCodeInConceptList(code, def, cc.getConcept())) {
+ if (cc.hasConcept() && validateCodeInConceptList(code, def, cc.getConcept(), altCodeRules)) {
return true;
}
}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java
index d7f06a032..cce645c47 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java
@@ -351,8 +351,16 @@ public class CompareUtilities extends BaseTestingUtilities {
private static boolean matches(String actualJsonString, String expectedJsonString) {
if (expectedJsonString.startsWith("$") && expectedJsonString.endsWith("$")) {
if (expectedJsonString.startsWith("$choice:")) {
- return Utilities.existsInList(actualJsonString, readChoices(expectedJsonString));
+ return Utilities.existsInList(actualJsonString, readChoices(8, expectedJsonString));
+ } else if (expectedJsonString.startsWith("$fragments:")) {
+ List fragments = readChoices(11, expectedJsonString);
+ for (String f : fragments) {
+ if (!actualJsonString.toLowerCase().contains(f.toLowerCase())) {
+ return false;
+ }
+ }
+ return true;
} else {
switch (expectedJsonString) {
case "$$" : return true;
@@ -367,9 +375,9 @@ public class CompareUtilities extends BaseTestingUtilities {
}
}
- private static List readChoices(String s) {
+ private static List readChoices(int offset, String s) {
List list = new ArrayList<>();
- s = s.substring(8, s.length()-1);
+ s = s.substring(offset, s.length()-1);
for (String p : s.split("\\|")) {
list.add(p);
}
diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java
index b11e701c9..f2345359a 100644
--- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java
+++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java
@@ -141,6 +141,8 @@ public class ToolingExtensions {
public static final String EXT_EXTENSION_STYLE = "http://hl7.org/fhir/tools/StructureDefinition/elementdefinition-extension-style";
public static final String EXT_LOGICAL_TARGET = "http://hl7.org/fhir/tools/StructureDefinition/logical-target";
public static final String EXT_PROFILE_MAPPING = "http://hl7.org/fhir/tools/StructureDefinition/profile-mapping";
+ public static final String EXT_CS_ALTERNATE_USE = "http://hl7.org/fhir/StructureDefinition/alternate-code-use";
+ public static final String EXT_CS_ALTERNATE_STATUS = "http://hl7.org/fhir/StructureDefinition/alternate-code-status";
// validated
// private static final String EXT_OID = "http://hl7.org/fhir/StructureDefinition/valueset-oid";
diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/UnicodeUtilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/UnicodeUtilities.java
index dbc28b86f..df5922a36 100644
--- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/UnicodeUtilities.java
+++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/UnicodeUtilities.java
@@ -57,8 +57,18 @@ public class UnicodeUtilities {
return list.isEmpty();
}
- public String summary() {
- return "Unicode Character "+describe(list.get(list.size()-1).c)+" at index "+list.get(list.size()-1).i+" has no terminating match";
+ public String summary(String src) {
+ char ch = list.get(list.size()-1).c;
+ int i = list.get(list.size()-1).i;
+ String pt;
+ if (i == 0) {
+ pt = "";
+ } else if (i < 5) {
+ pt = " (preceding text = '"+src.substring(0, i)+"')";
+ } else {
+ pt = " (preceding text = '"+src.substring(i-5, i)+"')";
+ }
+ return "Unicode Character "+describe(ch)+" at index "+i+" has no terminating match"+pt;
}
@@ -148,7 +158,7 @@ public class UnicodeUtilities {
if (ss.empty()) {
return null;
} else {
- return ss.summary();
+ return ss.summary(src);
}
}
diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java
index 0a48d8351..aa2b5c394 100644
--- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java
+++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java
@@ -34,19 +34,19 @@ import java.util.zip.ZipInputStream;
/*
Copyright (c) 2011+, HL7, Inc.
All rights reserved.
-
+
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this
+
+ * Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice,
+ * Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
- * Neither the name of HL7 nor the names of its contributors may be used to
+ * Neither the name of HL7 nor the names of its contributors may be used to
endorse or promote products derived from this software without specific
prior written permission.
-
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
@@ -57,7 +57,7 @@ import java.util.zip.ZipInputStream;
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
POSSIBILITY OF SUCH DAMAGE.
-
+
*/
@@ -376,7 +376,7 @@ public class Utilities {
}
public static boolean checkFolder(String dir, List errors)
- throws IOException {
+ throws IOException {
if (!new CSFile(dir).exists()) {
errors.add("Unable to find directory " + dir);
return false;
@@ -386,7 +386,7 @@ public class Utilities {
}
public static boolean checkFile(String purpose, String dir, String file, List errors)
- throws IOException {
+ throws IOException {
if (!new CSFile(dir + file).exists()) {
if (errors != null)
errors.add("Unable to find " + purpose + " file " + file + " in " + dir);
@@ -436,8 +436,8 @@ public class Utilities {
clearDirectory(fh.getAbsolutePath());
fh.delete();
}
- }
}
+ }
}
}
}
@@ -548,36 +548,36 @@ public class Utilities {
i++;
char ch = json.charAt(i);
switch (ch) {
- case '"':
- b.append('b');
- break;
- case '\\':
- b.append('\\');
- break;
- case '/':
- b.append('/');
- break;
- case 'b':
- b.append('\b');
- break;
- case 'f':
- b.append('\f');
- break;
- case 'n':
- b.append('\n');
- break;
- case 'r':
- b.append('\r');
- break;
- case 't':
- b.append('\t');
- break;
- case 'u':
- String hex = json.substring(i + 1, i + 5);
- b.append((char) Integer.parseInt(hex, 16));
- break;
- default:
- throw new FHIRException("Unknown JSON escape \\" + ch);
+ case '"':
+ b.append('b');
+ break;
+ case '\\':
+ b.append('\\');
+ break;
+ case '/':
+ b.append('/');
+ break;
+ case 'b':
+ b.append('\b');
+ break;
+ case 'f':
+ b.append('\f');
+ break;
+ case 'n':
+ b.append('\n');
+ break;
+ case 'r':
+ b.append('\r');
+ break;
+ case 't':
+ b.append('\t');
+ break;
+ case 'u':
+ String hex = json.substring(i + 1, i + 5);
+ b.append((char) Integer.parseInt(hex, 16));
+ break;
+ default:
+ throw new FHIRException("Unknown JSON escape \\" + ch);
}
} else
b.append(json.charAt(i));
@@ -590,7 +590,7 @@ public class Utilities {
public static boolean isPlural(String word) {
word = word.toLowerCase();
if ("restricts".equals(word) || "contains".equals(word) || "data".equals(word) || "specimen".equals(word) || "replaces".equals(word) || "addresses".equals(word)
- || "supplementalData".equals(word) || "instantiates".equals(word) || "imports".equals(word) || "covers".equals(word))
+ || "supplementalData".equals(word) || "instantiates".equals(word) || "imports".equals(word) || "covers".equals(word))
return false;
Inflector inf = new Inflector();
return !inf.singularize(word).equals(word);
@@ -653,10 +653,10 @@ public class Utilities {
@Deprecated
public static String uncheckedPath(String... args) {
return PathBuilder.getPathBuilder()
- .withRequireNonRootFirstEntry(false)
- .withRequireNonNullNonEmptyFirstEntry(false)
- .withRequirePathIsChildOfTarget(false)
- .buildPath(args);
+ .withRequireNonRootFirstEntry(false)
+ .withRequireNonNullNonEmptyFirstEntry(false)
+ .withRequirePathIsChildOfTarget(false)
+ .buildPath(args);
}
@@ -703,7 +703,7 @@ public class Utilities {
public static boolean isTokenChar(char ch) {
return isAlphabetic(ch) || (ch == '_');
}
-
+
public static boolean isDigit(char c) {
return (c >= '0') && (c <= '9');
}
@@ -1118,7 +1118,7 @@ public class Utilities {
}
return false;
}
-
+
public static boolean isAbsoluteUrlLinkable(String ref) {
if (ref != null && ref.contains(":")) {
String scheme = ref.substring(0, ref.indexOf(":"));
@@ -1173,7 +1173,7 @@ public class Utilities {
}
return b.toString();
}
-
+
public static String unCamelCase(String name) {
StringBuilder b = new StringBuilder();
boolean first = true;
@@ -1295,7 +1295,7 @@ public class Utilities {
Collections.sort(list);
return list;
}
-
+
public static List sorted(String[] set) {
List list = new ArrayList<>();
for (String s : set) {
@@ -1322,7 +1322,7 @@ public class Utilities {
return list;
}
-
+
public static void analyseStringDiffs(Set source, Set target, Set missed, Set extra) {
for (String s : source)
if (!target.contains(s))
@@ -1373,6 +1373,21 @@ public class Utilities {
}
}
+ public static String describeDuration(long ms) {
+ long days = ms / (1000 * 60 * 60 * 24);
+ long hours = ms / (1000 * 60 * 60) % 24;
+ long mins = ms / (1000 * 60) % 60;
+ long secs = ms / (1000) % 60;
+ ms = ms % 1000;
+ if (days > 0) {
+ return ""+days+"d "+hours+":"+mins+":"+secs+"."+ms;
+ } else {
+ return ""+hours+":"+mins+":"+secs+"."+ms;
+ }
+
+
+ }
+
public static boolean startsWithInList(String s, String... list) {
if (s == null) {
return false;
@@ -1410,7 +1425,7 @@ public class Utilities {
}
return false;
}
-
+
public static boolean endsWithInList(String s, Collection list) {
if (s == null) {
return false;
@@ -1493,15 +1508,15 @@ public class Utilities {
public static void unzip(InputStream zip, String target) throws IOException {
unzip(zip, Path.of(target));
}
-
+
public static void unzip(InputStream zip, Path target) throws IOException {
try (ZipInputStream zis = new ZipInputStream(zip)) {
ZipEntry zipEntry = zis.getNextEntry();
while (zipEntry != null) {
boolean isDirectory = false;
-
+
String n = makeOSSafe(zipEntry.getName());
-
+
if (n.endsWith(File.separator)) {
isDirectory = true;
}
@@ -1545,7 +1560,7 @@ public class Utilities {
}
final static int[] illegalChars = {34, 60, 62, 124, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 58, 42, 63, 92, 47};
-
+
static {
Arrays.sort(illegalChars);
}
@@ -1650,7 +1665,21 @@ public class Utilities {
if (b.length() == 19) {
b.append(".000");
}
- return applyDatePrecision(b.toString(), precision)+res[1];
+ String tz;
+ if (precision <= 10) {
+ tz = "";
+ } else {
+ tz = Utilities.noString(res[1]) ? defLowTimezone(b.toString()) : res[1];
+ }
+ return applyDatePrecision(b.toString(), precision)+tz;
+ }
+
+ private static String defLowTimezone(String string) {
+ return "+14:00"; // Kiribati permanent timezone since 1994 and we don't worry about before then
+ }
+
+ private static String defHighTimezone(String string) {
+ return "-12:00"; // Parts of Russia, Antarctica, Baker Island and Midway Atoll
}
public static String lowBoundaryForTime(String value, int precision) {
@@ -1683,7 +1712,7 @@ public class Utilities {
return applyTimePrecision(b.toString(), precision)+res[1];
}
-
+
private static Object applyDatePrecision(String v, int precision) {
switch (precision) {
case 4: return v.substring(0, 4);
@@ -1748,7 +1777,13 @@ public class Utilities {
if (b.length() == 19) {
b.append(".999");
}
- return applyDatePrecision(b.toString(), precision)+res[1];
+ String tz;
+ if (precision <= 10) {
+ tz = "";
+ } else {
+ tz = Utilities.noString(res[1]) ? defHighTimezone(b.toString()) : res[1];
+ }
+ return applyDatePrecision(b.toString(), precision)+tz;
}
private static String dayCount(int y, int m) {
@@ -1780,10 +1815,10 @@ public class Utilities {
}
}
-
+
private static String[] splitTimezone(String value) {
String[] res = new String[2];
-
+
if (value.contains("+")) {
res[0] = value.substring(0, value.indexOf("+"));
res[1] = value.substring(value.indexOf("+"));
@@ -1799,7 +1834,7 @@ public class Utilities {
}
return res;
}
-
+
public static Integer getDatePrecision(String value) {
return splitTimezone(value)[0].replace("-", "").replace("T", "").replace(":", "").replace(".", "").length();
}
@@ -1841,7 +1876,7 @@ public class Utilities {
* @return The resulting text.
*/
public static String appendDerivedTextToBase(@Nullable final String baseText,
- final String derivedText) {
+ final String derivedText) {
if (baseText == null) {
return derivedText.substring(3);
}
@@ -1888,13 +1923,13 @@ public class Utilities {
}
}
}
-
+
}
public static boolean isValidCRName(String name) {
return name != null && name.matches("[A-Z]([A-Za-z0-9_]){1,254}");
}
-
+
public static boolean isAllWhitespace(String s) {
if (Utilities.noString(s)) {
return true;
@@ -1906,7 +1941,7 @@ public class Utilities {
}
return true;
}
-
+
public static String trimWS(String s) {
if (Utilities.noString(s)) {
return s;
@@ -1927,7 +1962,7 @@ public class Utilities {
}
return s.substring(start, end+1);
}
-
+
// from https://en.wikipedia.org/wiki/Whitespace_character#Unicode
public static boolean isWhitespace(int ch) {
return Utilities.existsInList(ch, '\u0009', '\n', '\u000B','\u000C','\r','\u0020','\u0085','\u00A0',
@@ -2013,13 +2048,13 @@ public class Utilities {
-//public static boolean !isWhitespace(String s) {
-//boolean ok = true;
-//for (int i = 0; i < s.length(); i++)
-// ok = ok && Character.isWhitespace(s.charAt(i));
-//return ok;
-//
-//}
+ //public static boolean !isWhitespace(String s) {
+ //boolean ok = true;
+ //for (int i = 0; i < s.length(); i++)
+ // ok = ok && Character.isWhitespace(s.charAt(i));
+ //return ok;
+ //
+ //}
public static boolean isTxFhirOrgServer(String s) {
@@ -2042,30 +2077,37 @@ public class Utilities {
return i == 0 ? "" : s.substring(0, i+1);
}
-public static void renameDirectory(String source, String dest) throws FHIRException, IOException {
- File src = new File(source);
- File dst = new File(dest);
- if (!src.renameTo(dst)) {
- int i = 0;
- do {
- try {
- Thread.sleep(20);
+ public static void renameDirectory(String source, String dest) throws FHIRException, IOException {
+ File src = new File(source);
+ File dst = new File(dest);
+ if (!src.renameTo(dst)) {
+ int i = 0;
+ do {
+ try {
+ Thread.sleep(20);
} catch (Exception e) {
// nothing
}
- System.gc();
- i++;
- } while (!src.renameTo(dst) && i < 10);
- if (src.exists()) {
- copyDirectory(source, dest, null);
- try {
- src.delete();
- } catch (Exception e) {
- // nothing
- }
- }
- }
-
-}
+ System.gc();
+ i++;
+ } while (!src.renameTo(dst) && i < 10);
+ if (src.exists()) {
+ copyDirectory(source, dest, null);
+ try {
+ src.delete();
+ } catch (Exception e) {
+ // nothing
+ }
+ }
+ }
+
+ }
+
+ public static String urlTail(String url) {
+ if (url == null) {
+ return null;
+ }
+ return url.contains("/") ? url.substring(url.lastIndexOf("/")+1) : url;
+ }
}
diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java
index 0365ab0e3..2b27e79a1 100644
--- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java
+++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java
@@ -921,6 +921,7 @@ public class I18nConstants {
public static final String EXTENSION_CONTEXT_UNABLE_TO_FIND_PROFILE = "EXTENSION_CONTEXT_UNABLE_TO_FIND_PROFILE";
public static final String TERMINOLOGY_TX_HINT = "TERMINOLOGY_TX_HINT";
public static final String TERMINOLOGY_TX_WARNING = "TERMINOLOGY_TX_WARNING";
+ public static final String SD_ED_TYPE_PROFILE_WRONG_TYPE = "SD_ED_TYPE_PROFILE_WRONG_TYPE";
}
diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties
index 0dd8228e2..5fa6b2064 100644
--- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties
+++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties
@@ -739,8 +739,8 @@ BUNDLE_SEARCH_NO_MODE = SearchSet bundles should have search modes on the entrie
INV_FAILED = Rule {0} Failed
PATTERN_CHECK_STRING = The pattern [{0}] defined in the profile {1} not found. Issues: {2}
TYPE_SPECIFIC_CHECKS_DT_URL_EXAMPLE = Example URLs are not allowed in this context ({0})
-UNICODE_BIDI_CONTROLS_CHARS_DISALLOWED = The Unicode sequence has bi-di control characters which are not allowed in this context: {1}
-UNICODE_BIDI_CONTROLS_CHARS_MATCH = The Unicode sequence has unterminated bi-di control characters (see CVE-2021-42574): {1}
+UNICODE_BIDI_CONTROLS_CHARS_DISALLOWED = The Unicode sequence has bi-di control characters which are not allowed in this context: {0}
+UNICODE_BIDI_CONTROLS_CHARS_MATCH = The Unicode sequence has unterminated bi-di control characters (see CVE-2021-42574): {0}
CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHALL = HL7 Defined CodeSystems SHALL have a stated value for the {0} element so that users know the status and meaning of the code system clearly
CODESYSTEM_CS_HL7_MISSING_ELEMENT_SHOULD = HL7 Defined CodeSystems SHOULD have a stated value for the {0} element so that users know the status and meaning of the code system clearly
CODESYSTEM_CS_NONHL7_MISSING_ELEMENT = CodeSystems SHOULD have a stated value for the {0} element so that users know the status and meaning of the code system clearly
@@ -975,3 +975,5 @@ ED_INVARIANT_EXPRESSION_ERROR = Error in invariant ''{0}'' with expression ''{1}
SNAPSHOT_IS_EMPTY = The snapshot for the profile ''{0}'' is empty (which should not happen)
TERMINOLOGY_TX_HINT = {1}
TERMINOLOGY_TX_WARNING = {1}
+SD_ED_TYPE_WRONG_TYPE_one = The element has a type {0} which is different to the type {1} on the base profile {2}
+SD_ED_TYPE_WRONG_TYPE_other = The element has a type {0} which is not in the types {1} on the base profile {2}
diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UnicodeUtilitiesTests.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UnicodeUtilitiesTests.java
index f4f214390..17e539173 100644
--- a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UnicodeUtilitiesTests.java
+++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UnicodeUtilitiesTests.java
@@ -22,7 +22,7 @@ public class UnicodeUtilitiesTests {
Assertions.assertNull(UnicodeUtilities.checkUnicodeWellFormed(UnicodeUtilities.RLI + " "+ UnicodeUtilities.LRI + "a b c "+
UnicodeUtilities.PDI+" "+UnicodeUtilities.LRI+" d e f "+UnicodeUtilities.PDI+" "+UnicodeUtilities.PDI));
Assertions.assertEquals(UnicodeUtilities.checkUnicodeWellFormed("'''subject funds from back account then "+UnicodeUtilities.RLI + "''' ;return"),
- "Unicode Character RLI at index 40 has no terminating match");
+ "Unicode Character RLI at index 40 has no terminating match (preceding text = 'then ')");
}
}
diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java
index 8276d6e2a..9323a4efc 100644
--- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java
+++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java
@@ -368,10 +368,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
if (externalHostServices != null) {
- return externalHostServices.resolveReference(c.getAppContext(), url, refContext);
+ return setParentsBase(externalHostServices.resolveReference(c.getAppContext(), url, refContext));
} else if (fetcher != null) {
try {
- return fetcher.fetch(InstanceValidator.this, c.getAppContext(), url);
+ return setParents(fetcher.fetch(InstanceValidator.this, c.getAppContext(), url));
} catch (IOException e) {
throw new FHIRException(e);
}
@@ -5620,11 +5620,12 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
boolean checkDisplay = true;
SpecialElement special = ei.getElement().getSpecial();
- if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.BUNDLE_ISSUES || special == SpecialElement.PARAMETER) {
- ok = checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, ei.getElement(), ei.getElement(), localStack, false) && ok;
- } else {
- ok = checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, resource, ei.getElement(), localStack, false) && ok;
- }
+ // this used to say
+ // if (special == SpecialElement.BUNDLE_ENTRY || special == SpecialElement.BUNDLE_OUTCOME || special == SpecialElement.BUNDLE_ISSUES || special == SpecialElement.PARAMETER) {
+ // ok = checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, ei.getElement(), ei.getElement(), localStack, false) && ok;
+ // but this isn't correct - when the invariant is on the element, the invariant is in the context of the resource that contains the element.
+ // changed 18-Jul 2023 - see https://chat.fhir.org/#narrow/stream/179266-fhirpath/topic/FHIRPath.20.25resource.20variable
+ ok = checkInvariants(hostContext, errors, profile, typeDefn != null ? typeDefn : checkDefn, resource, ei.getElement(), localStack, false) && ok;
ei.getElement().markValidation(profile, checkDefn);
boolean elementValidated = false;
@@ -5770,8 +5771,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
int index = profile.getSnapshot().getElement().indexOf(checkDefn);
if (index < profile.getSnapshot().getElement().size() - 1) {
String nextPath = profile.getSnapshot().getElement().get(index + 1).getPath();
- if (!nextPath.equals(checkDefn.getPath()) && nextPath.startsWith(checkDefn.getPath()))
- ok = validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
+ if (!nextPath.equals(checkDefn.getPath()) && nextPath.startsWith(checkDefn.getPath())) {
+ if (ei.getElement().getSpecial() == SpecialElement.BUNDLE_ENTRY || ei.getElement().getSpecial() == SpecialElement.BUNDLE_OUTCOME || ei.getElement().getSpecial() == SpecialElement.PARAMETER) {
+ ok = validateElement(hostContext.forEntry(ei.getElement(), null), errors, profile, checkDefn, null, null, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
+ } else if (ei.getElement().getSpecial() == SpecialElement.CONTAINED) {
+ ok = validateElement(hostContext.forContained(ei.getElement()), errors, profile, checkDefn, null, null, ei.getElement(), ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
+ } else {
+ ok = validateElement(hostContext, errors, profile, checkDefn, null, null, resource, ei.getElement(), type, localStack, thisIsCodeableConcept, checkDisplay, thisExtension, pct, mode) && ok;
+ }
+ }
}
}
return ok;
@@ -6560,11 +6568,19 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
this.allowDoubleQuotesInFHIRPath = allowDoubleQuotesInFHIRPath;
}
- public static void setParents(Element element) {
+ public static Element setParents(Element element) {
if (element != null && !element.hasParentForValidator()) {
element.setParentForValidator(null);
setParentsInner(element);
}
+ return element;
+ }
+
+ public static Base setParentsBase(Base element) {
+ if (element instanceof Element) {
+ setParents((Element) element);
+ }
+ return element;
}
public static void setParentsInner(Element element) {
diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java
index da8783515..4b155f5a8 100644
--- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java
+++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureDefinitionValidator.java
@@ -28,6 +28,7 @@ import org.hl7.fhir.r5.formats.IParser.OutputStyle;
import org.hl7.fhir.r5.model.Base;
import org.hl7.fhir.r5.model.Coding;
import org.hl7.fhir.r5.model.ElementDefinition;
+import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent;
import org.hl7.fhir.r5.model.ExpressionNode;
import org.hl7.fhir.r5.model.Extension;
import org.hl7.fhir.r5.model.Resource;
@@ -760,6 +761,9 @@ public class StructureDefinitionValidator extends BaseValidator {
String code = type.getNamedChildValue("code");
if (code == null && path != null) {
code = getTypeCodeFromSD(sd, path);
+ } else {
+ Set types = getTypeCodesFromSD(sd, path);
+ ok = rulePlural(errors, NO_RULE_DATE, IssueType.EXCEPTION, stack.getLiteralPath(), types.isEmpty() || types.contains(code), types.size(), I18nConstants.SD_ED_TYPE_PROFILE_WRONG_TYPE, code, types.toString(), sd.getVersionedUrl());
}
if (code != null) {
List profiles = type.getChildrenByName("profile");
@@ -778,7 +782,7 @@ public class StructureDefinitionValidator extends BaseValidator {
}
}
}
- return true;
+ return ok;
}
private boolean validateProfileTypeOrTarget(List errors, Element profile, String code, NodeStack stack, String path) {
@@ -833,6 +837,18 @@ public class StructureDefinitionValidator extends BaseValidator {
return ed != null && ed.getType().size() == 1 ? ed.getTypeFirstRep().getCode() : null;
}
+ private Set getTypeCodesFromSD(StructureDefinition sd, String path) {
+ Set codes = new HashSet<>();
+ for (ElementDefinition t : sd.getSnapshot().getElement()) {
+ if (t.hasPath() && t.getPath().equals(path)) {
+ for (TypeRefComponent tr : t.getType()) {
+ codes.add(tr.getCode());
+ }
+ }
+ }
+ return codes;
+ }
+
private boolean validateTypeProfile(List errors, Element profile, String code, NodeStack stack, String path) {
boolean ok = true;
String p = profile.primitiveValue();
diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java
index 83a180049..3f152ba8c 100644
--- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java
+++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/ValidatorHostContext.java
@@ -30,14 +30,23 @@ public class ValidatorHostContext {
this.appContext = appContext;
this.resource = element;
this.rootResource = element;
+ check();
+
// no groupingResource (Bundle or Parameters)
dump("creating");
}
+ private void check() {
+ if (!rootResource.hasParentForValidator()) {
+ throw new Error("No parent on root resource");
+ }
+ }
+
public ValidatorHostContext(Object appContext, Element element, Element root) {
this.appContext = appContext;
this.resource = element;
this.rootResource = root;
+ check();
// no groupingResource (Bundle or Parameters)
dump("creating");
}
@@ -47,6 +56,7 @@ public class ValidatorHostContext {
this.resource = element;
this.rootResource = root;
this.groupingResource = groupingResource;
+ check();
dump("creating");
}
diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterScrubbers.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterScrubbers.java
index 17e1af7ca..b5e7f8729 100644
--- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterScrubbers.java
+++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterScrubbers.java
@@ -47,6 +47,8 @@ public class TxTesterScrubbers {
"http://hl7.org/fhir/test/ValueSet/extensions-bad-supplement",
"http://hl7.org/fhir/test/ValueSet/simple-all",
"http://hl7.org/fhir/test/ValueSet/simple-enumerated",
+ "http://hl7.org/fhir/StructureDefinition/alternate-code-use",
+ "http://hl7.org/fhir/StructureDefinition/alternate-code-status",
"http://hl7.org/fhir/test/ValueSet/simple-filter-isa");
}
diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java
index dd7959496..87a2b62bc 100644
--- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java
+++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/TerminologyServiceTests.java
@@ -29,6 +29,7 @@ import org.hl7.fhir.r5.model.OperationOutcome;
import org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity;
import org.hl7.fhir.r5.model.OperationOutcome.IssueType;
import org.hl7.fhir.r5.model.OperationOutcome.OperationOutcomeIssueComponent;
+import org.hl7.fhir.r5.model.Parameters.ParametersParameterComponent;
import org.hl7.fhir.r5.model.Resource;
import org.hl7.fhir.r5.model.UriType;
import org.hl7.fhir.r5.model.ValueSet;
@@ -122,6 +123,7 @@ public class TerminologyServiceTests {
}
ValidationEngine engine = new ValidationEngine(this.baseEngine);
for (String s : setup.suite.forceArray("setup").asStrings()) {
+ // System.out.println(s);
Resource res = loadResource(s);
engine.seeResource(res);
}
@@ -243,7 +245,13 @@ public class TerminologyServiceTests {
}
if (p.hasParameter("mode") && "lenient-display-validation".equals(p.getParameterString("mode"))) {
options = options.setDisplayWarningMode(true);
- }
+ }
+ engine.getContext().getExpansionParameters().clearParameters("includeAlternateCodes");
+ for (ParametersParameterComponent pp : p.getParameter()) {
+ if ("includeAlternateCodes".equals(pp.getName())) {
+ engine.getContext().getExpansionParameters().addParameter(pp.copy());
+ }
+ }
ValidationResult vm;
if (p.hasParameter("code")) {
vm = engine.getContext().validateCode(options.withGuessSystem(), p.getParameterString("system"), p.getParameterString("systemVersion"), p.getParameterString("code"), p.getParameterString("display"), vs);