Merge pull request #1353 from hapifhir/2023-07-gg-context-fixes

2023 07 gg context fixes
This commit is contained in:
Grahame Grieve 2023-07-21 14:48:19 +10:00 committed by GitHub
commit 4e8f0f0c0c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
39 changed files with 1685 additions and 539 deletions

View File

@ -111,6 +111,14 @@
<artifactId>junit-jupiter-params</artifactId>
<scope>test</scope>
</dependency>
<!-- https://mvnrepository.com/artifact/org.xerial/sqlite-jdbc -->
<dependency>
<groupId>org.xerial</groupId>
<artifactId>sqlite-jdbc</artifactId>
<version>3.42.0.0</version>
</dependency>
</dependencies>
<build>

View File

@ -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()));
}

View File

@ -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<Integer, ConceptDefinitionComponent> 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);
}
}

View File

@ -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<String, String> relationships = new HashMap<>();
private Map<String, String> vocabularies = new HashMap<>();
private Map<String, String> domains = new HashMap<>();
private Map<String, String> 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");
}
}

View File

@ -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())) {

View File

@ -1032,4 +1032,11 @@ private Map<String, Object> userData;
return null;
}
public abstract Base copy();
public void copyValues(Base dst) {
}
}

View File

@ -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";

View File

@ -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;
}
}

View File

@ -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<Base> funcLowBoundary(ExecutionContext context, List<Base> 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<Base> funcHighBoundary(ExecutionContext context, List<Base> 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<Base> result = new ArrayList<Base>();
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<Base> funcJoin(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
List<Base> 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<Base> result = new ArrayList<Base>();
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<Base> funcComparable(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
if (focus.size() != 1 || !(focus.get(0).fhirType().equals("Quantity"))) {
return makeBoolean(false);
}
List<Base> 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) {

View File

@ -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");
}
}

View File

@ -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())) {

View File

@ -339,6 +339,8 @@ private Map<String, Object> userData;
return null;
}
public abstract Base copy();
public void copyValues(Base dst) {
}

View File

@ -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";

View File

@ -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;
}
}

View File

@ -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<Base> funcLowBoundary(ExecutionContext context, List<Base> 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<Base> funcHighBoundary(ExecutionContext context, List<Base> 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<Base> result = new ArrayList<Base>();
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<Base> funcJoin(ExecutionContext context, List<Base> focus, ExpressionNode exp) {
List<Base> 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<Base> result = new ArrayList<Base>();
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<Base> funcComparable(ExecutionContext context, List<Base> focus, ExpressionNode exp) throws FHIRException {
if (focus.size() != 1 || !(focus.get(0).fhirType().equals("Quantity"))) {
return makeBoolean(false);
}
List<Base> 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) {

View File

@ -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;

View File

@ -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;

View File

@ -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());
}
}
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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<DataType> getParameterValues(String name) {
List<DataType> res = new ArrayList<>();
for (ParametersParameterComponent p : getParameter()) {
if (p.getName().equals(name))
res.add(p.getValue());
}
return res;
}
public List<ParametersParameterComponent> getParameters(String name) {
List<ParametersParameterComponent> res = new ArrayList<ParametersParameterComponent>();
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<DataType> getParameterValues(String name) {
List<DataType> res = new ArrayList<>();
for (ParametersParameterComponent p : getParameter()) {
if (p.getName().equals(name))
res.add(p.getValue());
}
return res;
}
public List<ParametersParameterComponent> getParameters(String name) {
List<ParametersParameterComponent> res = new ArrayList<ParametersParameterComponent>();
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
}

View File

@ -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<Extension> list) {
seeRootObligations(eid, list, null, false, "$all");
}
public void seeObligations(List<Extension> list, List<Extension> compList, boolean compare, String id) {
HashMap<String, ObligationDetail> compBindings = new HashMap<String, ObligationDetail>();
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<Extension> list, List<Extension> compList, boolean compare, String id) {
HashMap<String, ObligationDetail> compBindings = new HashMap<String, ObligationDetail>();
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;

View File

@ -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) {

View File

@ -466,6 +466,31 @@ public class CodeSystemUtilities {
return null;
}
public static ConceptDefinitionComponent findCodeOrAltCode(List<ConceptDefinitionComponent> 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()) {

View File

@ -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<ValueSet> filters, boolean noInactive, List<ValueSetExpansionPropertyComponent> 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<String> getCodesForConcept(ValueSetExpansionContainsComponent focus, Parameters expParams) {
List<String> 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<ConceptDefinitionDesignationComponent> convert(List<ConceptReferenceDesignationComponent> designations) {
List<ConceptDefinitionDesignationComponent> list = new ArrayList<ConceptDefinitionDesignationComponent>();
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<String> getCodesForConcept(ConceptDefinitionComponent focus, Parameters expParams) {
List<String> 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<String> 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<ValueSetExpansionParameterComponent> params, Parameters expParams, List<ValueSet> filters, boolean noInactive, List<ValueSetExpansionPropertyComponent> 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);
}
}
}
}

View File

@ -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<String> 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<Extension> 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);
}

View File

@ -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<CodeSystem> localSystems = new ArrayList<>();
Parameters expansionProfile;
protected Parameters expansionProfile;
private TerminologyCapabilities txCaps;
private Set<String> 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<ConceptDefinitionComponent> children = (List<ConceptDefinitionComponent>) 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<ConceptDefinitionComponent> concept, String code) {
private ConceptDefinitionComponent findCodeInConcept(List<ConceptDefinitionComponent> 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<String> alternateCodes(ConceptDefinitionComponent focus, AlternateCodesProcessingRules altCodeRules) {
List<String> 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<String> problems) {
Set<String> 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<ConceptDefinitionComponent> 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<ConceptDefinitionComponent> list) {
public boolean validateCodeInConceptList(String code, CodeSystem def, List<ConceptDefinitionComponent> 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;
}
}

View File

@ -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<String> 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<String> readChoices(String s) {
private static List<String> readChoices(int offset, String s) {
List<String> 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);
}

View File

@ -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";

View File

@ -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);
}
}

View File

@ -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<String> 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<String> 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<String> sorted(String[] set) {
List<String> list = new ArrayList<>();
for (String s : set) {
@ -1322,7 +1322,7 @@ public class Utilities {
return list;
}
public static void analyseStringDiffs(Set<String> source, Set<String> target, Set<String> missed, Set<String> 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<String> 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;
}
}

View File

@ -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";
}

View File

@ -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}

View File

@ -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 ')");
}
}

View File

@ -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) {

View File

@ -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<String> 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<Element> profiles = type.getChildrenByName("profile");
@ -778,7 +782,7 @@ public class StructureDefinitionValidator extends BaseValidator {
}
}
}
return true;
return ok;
}
private boolean validateProfileTypeOrTarget(List<ValidationMessage> 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<String> getTypeCodesFromSD(StructureDefinition sd, String path) {
Set<String> 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<ValidationMessage> errors, Element profile, String code, NodeStack stack, String path) {
boolean ok = true;
String p = profile.primitiveValue();

View File

@ -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");
}

View File

@ -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");
}

View File

@ -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);