fix processing of modifier extensions and cross-version modifier extensions

This commit is contained in:
Grahame Grieve 2021-11-21 19:37:05 +11:00
parent 85df4b716b
commit 41b7a0566c
7 changed files with 408 additions and 8 deletions

View File

@ -1,5 +1,5 @@
Validator:
* no changes
* fix processing of modifier extensions and cross-version modifier extensions
Other changes:
* improvements to data types rendering based on new test cases (URLs, Money, Markdown)

View File

@ -0,0 +1,387 @@
package org.hl7.fhir.convertors.misc;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import javax.naming.ldap.StartTlsRequest;
import javax.xml.parsers.ParserConfigurationException;
import org.hl7.fhir.exceptions.FHIRFormatError;
import org.hl7.fhir.utilities.Utilities;
import org.hl7.fhir.utilities.json.JsonTrackingParser;
import org.xml.sax.SAXException;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
public class XVerPackegeFixer {
private static final String R5_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r5.core\\package";
private static final String R4_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r4.core\\package";
private static final String R3_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r3.core\\package";
private static final String R2B_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r2b.core\\package";
private static final String R2_FOLDER = "C:\\work\\org.hl7.fhir\\packages\\hl7.fhir.rX\\hl7.fhir.r2.core\\package";
private static int mod;
private static Map<String, org.hl7.fhir.r5.model.StructureDefinition> map5 = new HashMap<>();
private static Map<String, org.hl7.fhir.r4.model.StructureDefinition> map4 = new HashMap<>();
private static Map<String, org.hl7.fhir.dstu3.model.StructureDefinition> map3 = new HashMap<>();
private static Map<String, org.hl7.fhir.dstu2.model.StructureDefinition> map2 = new HashMap<>();
private static Map<String, org.hl7.fhir.dstu2016may.model.StructureDefinition> map2b = new HashMap<>();
public static void main(String[] args) throws FileNotFoundException, ParserConfigurationException, SAXException, IOException {
mod = 0;
for (File f : new File(args[0]).listFiles()) {
if (f.getName().startsWith("xver-")) {
JsonObject j = JsonTrackingParser.parseJson(f);
fixUp(j, f.getName());
JsonTrackingParser.write(j, f, true);
}
}
System.out.println("all done: "+mod+" modifiers");
}
private static void fixUp(JsonObject j, String name) throws FHIRFormatError, FileNotFoundException, IOException {
name = name.replace(".json", "");
System.out.println("Process "+name);
String version = name.substring(name.lastIndexOf("-")+1);
int i = 0;
for (Entry<String, JsonElement> e : j.entrySet()) {
if (i == 50) {
i = 0;
System.out.print(".");
}
i++;
String n = e.getKey();
JsonObject o = ((JsonObject) e.getValue());
boolean ok = (o.has("types") && o.getAsJsonArray("types").size() > 0) || (o.has("elements") && o.getAsJsonArray("elements").size() > 0);
if (!ok) {
List<String> types = new ArrayList<>();
List<String> elements = new ArrayList<>();
getElementInfo(version, n, types, elements);
if (elements.size() > 0) {
JsonArray arr = o.getAsJsonArray("elements");
if (arr == null) {
arr = new JsonArray();
o.add("elements", arr);
}
for (String s : types) {
arr.add(s);
}
} else if (types.size() > 0) {
JsonArray arr = o.getAsJsonArray("types");
if (arr == null) {
arr = new JsonArray();
o.add("types", arr);
}
for (String s : types) {
arr.add(s);
}
}
}
}
System.out.println("done");
}
private static boolean getElementInfo(String version, String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
if ("contained".equals(n.substring(n.indexOf(".")+1))) {
return false;
}
switch (version) {
case "4.6": return getElementInfoR5(n, types, elements);
case "4.0": return getElementInfoR4(n, types, elements);
case "3.0": return getElementInfoR3(n, types, elements);
case "1.4": return getElementInfoR2B(n, types, elements);
case "1.0": return getElementInfoR2(n, types, elements);
}
return false;
}
private static Object tail(String value) {
return value.contains("/") ? value.substring(value.lastIndexOf("/")+1) : value;
}
private static boolean getElementInfoR5(String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
String tn = n.substring(0, n.indexOf("."));
org.hl7.fhir.r5.model.StructureDefinition sd = null;
if (map5.containsKey(tn)) {
sd = map5.get(tn);
} else {
sd = (org.hl7.fhir.r5.model.StructureDefinition) new org.hl7.fhir.r5.formats.JsonParser().parse(new FileInputStream(Utilities.path(R5_FOLDER, "StructureDefinition-"+tn+".json")));
map5.put(tn, sd);
}
for (org.hl7.fhir.r5.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(n)) {
List<org.hl7.fhir.r5.model.ElementDefinition> children = listChildrenR5(sd.getSnapshot().getElement(), ed);
if (children.size() > 0) {
for (org.hl7.fhir.r5.model.ElementDefinition c : children) {
String en = c.getPath().substring(ed.getPath().length()+1);
elements.add(en);
}
} else {
for (org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
if (t.hasTargetProfile()) {
StringBuilder b = new StringBuilder();
b.append(t.getWorkingCode());
b.append("(");
boolean first = true;
for (org.hl7.fhir.r5.model.CanonicalType u : t.getTargetProfile()) {
if (first) first = false; else b.append("|");
b.append(tail(u.getValue()));
}
b.append(")");
types.add(b.toString());
} else {
types.add(t.getWorkingCode());
}
}
}
}
}
return false;
}
private static List<org.hl7.fhir.r5.model.ElementDefinition> listChildrenR5(List<org.hl7.fhir.r5.model.ElementDefinition> list, org.hl7.fhir.r5.model.ElementDefinition ed) {
List<org.hl7.fhir.r5.model.ElementDefinition> res = new ArrayList<>();
for (org.hl7.fhir.r5.model.ElementDefinition t : list) {
String p = t.getPath();
if (p.startsWith(ed.getPath()+".")) {
p = p.substring(ed.getPath().length()+1);
if (!p.contains(".")) {
res.add(t);
}
}
}
return res;
}
private static boolean getElementInfoR4(String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
String tn = n.substring(0, n.indexOf("."));
org.hl7.fhir.r4.model.StructureDefinition sd = null;
if (map4.containsKey(tn)) {
sd = map4.get(tn);
} else {
sd = (org.hl7.fhir.r4.model.StructureDefinition) new org.hl7.fhir.r4.formats.JsonParser().parse(new FileInputStream(Utilities.path(R4_FOLDER, "StructureDefinition-"+tn+".json")));
map4.put(tn, sd);
}
for (org.hl7.fhir.r4.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(n)) {
List<org.hl7.fhir.r4.model.ElementDefinition> children = listChildrenR4(sd.getSnapshot().getElement(), ed);
if (children.size() > 0) {
for (org.hl7.fhir.r4.model.ElementDefinition c : children) {
String en = c.getPath().substring(ed.getPath().length()+1);
elements.add(en);
}
} else {
for (org.hl7.fhir.r4.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
if (t.hasTargetProfile()) {
StringBuilder b = new StringBuilder();
b.append(t.getWorkingCode());
b.append("(");
boolean first = true;
for (org.hl7.fhir.r4.model.CanonicalType u : t.getTargetProfile()) {
if (first) first = false; else b.append("|");
b.append(tail(u.getValue()));
}
b.append(")");
types.add(b.toString());
} else {
types.add(t.getWorkingCode());
}
}
}
}
}
return false;
}
private static List<org.hl7.fhir.r4.model.ElementDefinition> listChildrenR4(List<org.hl7.fhir.r4.model.ElementDefinition> list, org.hl7.fhir.r4.model.ElementDefinition ed) {
List<org.hl7.fhir.r4.model.ElementDefinition> res = new ArrayList<>();
for (org.hl7.fhir.r4.model.ElementDefinition t : list) {
String p = t.getPath();
if (p.startsWith(ed.getPath()+".")) {
p = p.substring(ed.getPath().length()+1);
if (!p.contains(".")) {
res.add(t);
}
}
}
return res;
}
private static boolean getElementInfoR3(String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
String tn = n.substring(0, n.indexOf("."));
org.hl7.fhir.dstu3.model.StructureDefinition sd = null;
if (map3.containsKey(tn)) {
sd = map3.get(tn);
} else {
sd = (org.hl7.fhir.dstu3.model.StructureDefinition) new org.hl7.fhir.dstu3.formats.JsonParser().parse(new FileInputStream(Utilities.path(R3_FOLDER, "StructureDefinition-"+tn+".json")));
map3.put(tn, sd);
}
for (org.hl7.fhir.dstu3.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(n)) {
List<org.hl7.fhir.dstu3.model.ElementDefinition> children = listChildrenR3(sd.getSnapshot().getElement(), ed);
if (children.size() > 0) {
for (org.hl7.fhir.dstu3.model.ElementDefinition c : children) {
String en = c.getPath().substring(ed.getPath().length()+1);
elements.add(en);
}
} else {
for (org.hl7.fhir.dstu3.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
if (t.hasTargetProfile()) {
StringBuilder b = new StringBuilder();
b.append(t.getCode());
b.append("(");
b.append(tail(t.getTargetProfile()));
b.append(")");
types.add(b.toString());
} else {
types.add(t.getCode());
}
}
}
}
}
return false;
}
private static List<org.hl7.fhir.dstu3.model.ElementDefinition> listChildrenR3(List<org.hl7.fhir.dstu3.model.ElementDefinition> list, org.hl7.fhir.dstu3.model.ElementDefinition ed) {
List<org.hl7.fhir.dstu3.model.ElementDefinition> res = new ArrayList<>();
for (org.hl7.fhir.dstu3.model.ElementDefinition t : list) {
String p = t.getPath();
if (p.startsWith(ed.getPath()+".")) {
p = p.substring(ed.getPath().length()+1);
if (!p.contains(".")) {
res.add(t);
}
}
}
return res;
}
private static boolean getElementInfoR2(String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
String tn = n.substring(0, n.indexOf("."));
org.hl7.fhir.dstu2.model.StructureDefinition sd = null;
if (map2.containsKey(tn)) {
sd = map2.get(tn);
} else {
sd = (org.hl7.fhir.dstu2.model.StructureDefinition) new org.hl7.fhir.dstu2.formats.JsonParser().parse(new FileInputStream(Utilities.path(R2_FOLDER, "StructureDefinition-"+tn+".json")));
map2.put(tn, sd);
}
for (org.hl7.fhir.dstu2.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(n)) {
List<org.hl7.fhir.dstu2.model.ElementDefinition> children = listChildrenR2(sd.getSnapshot().getElement(), ed);
if (children.size() > 0) {
for (org.hl7.fhir.dstu2.model.ElementDefinition c : children) {
String en = c.getPath().substring(ed.getPath().length()+1);
elements.add(en);
}
} else {
for (org.hl7.fhir.dstu2.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
if (t.hasProfile()) {
StringBuilder b = new StringBuilder();
b.append(t.getCode());
b.append("(");
boolean first = true;
for (org.hl7.fhir.dstu2.model.UriType u : t.getProfile()) {
if (first) first = false; else b.append("|");
b.append(tail(u.getValue()));
}
b.append(")");
types.add(b.toString());
} else {
types.add(t.getCode());
}
}
}
}
}
return false;
}
private static List<org.hl7.fhir.dstu2.model.ElementDefinition> listChildrenR2(List<org.hl7.fhir.dstu2.model.ElementDefinition> list, org.hl7.fhir.dstu2.model.ElementDefinition ed) {
List<org.hl7.fhir.dstu2.model.ElementDefinition> res = new ArrayList<>();
for (org.hl7.fhir.dstu2.model.ElementDefinition t : list) {
String p = t.getPath();
if (p.startsWith(ed.getPath()+".")) {
p = p.substring(ed.getPath().length()+1);
if (!p.contains(".")) {
res.add(t);
}
}
}
return res;
}
private static boolean getElementInfoR2B(String n, List<String> types, List<String> elements) throws FHIRFormatError, FileNotFoundException, IOException {
String tn = n.substring(0, n.indexOf("."));
org.hl7.fhir.dstu2016may.model.StructureDefinition sd = null;
if (map2b.containsKey(tn)) {
sd = map2b.get(tn);
} else {
sd = (org.hl7.fhir.dstu2016may.model.StructureDefinition) new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(new FileInputStream(Utilities.path(R2B_FOLDER, "StructureDefinition-"+tn+".json")));
map2b.put(tn, sd);
}
for (org.hl7.fhir.dstu2016may.model.ElementDefinition ed : sd.getSnapshot().getElement()) {
if (ed.getPath().equals(n)) {
List<org.hl7.fhir.dstu2016may.model.ElementDefinition> children = listChildrenR2B(sd.getSnapshot().getElement(), ed);
if (children.size() > 0) {
for (org.hl7.fhir.dstu2016may.model.ElementDefinition c : children) {
String en = c.getPath().substring(ed.getPath().length()+1);
elements.add(en);
}
} else {
for (org.hl7.fhir.dstu2016may.model.ElementDefinition.TypeRefComponent t : ed.getType()) {
if (t.hasProfile()) {
StringBuilder b = new StringBuilder();
b.append(t.getCode());
b.append("(");
boolean first = true;
for (org.hl7.fhir.dstu2016may.model.UriType u : t.getProfile()) {
if (first) first = false; else b.append("|");
b.append(tail(u.getValue()));
}
b.append(")");
types.add(b.toString());
} else {
types.add(t.getCode());
}
}
}
}
}
return false;
}
private static List<org.hl7.fhir.dstu2016may.model.ElementDefinition> listChildrenR2B(List<org.hl7.fhir.dstu2016may.model.ElementDefinition> list, org.hl7.fhir.dstu2016may.model.ElementDefinition ed) {
List<org.hl7.fhir.dstu2016may.model.ElementDefinition> res = new ArrayList<>();
for (org.hl7.fhir.dstu2016may.model.ElementDefinition t : list) {
String p = t.getPath();
if (p.startsWith(ed.getPath()+".")) {
p = p.substring(ed.getPath().length()+1);
if (!p.contains(".")) {
res.add(t);
}
}
}
return res;
}
}

View File

@ -133,6 +133,11 @@ public class XVerExtensionManager {
} else {
throw new FHIRException("Internal error - attempt to define extension for "+url+" when it is invalid");
}
if (path.has("modifier") && path.get("modifier").getAsBoolean()) {
ElementDefinition baseDef = new ElementDefinition("Extension");
sd.getDifferential().getElement().add(0, baseDef);
baseDef.setIsModifier(true);
}
return sd;
}

View File

@ -664,6 +664,12 @@ public class JsonTrackingParser {
TextFile.stringToFile(jcnt, file);
}
public static void write(JsonObject json, File file, boolean pretty) throws IOException {
Gson gson = pretty ? new GsonBuilder().setPrettyPrinting().create() : new GsonBuilder().create();
String jcnt = gson.toJson(json);
TextFile.stringToFile(jcnt, file);
}
public static void write(JsonObject json, String fileName) throws IOException {
Gson gson = new GsonBuilder().setPrettyPrinting().create();
String jcnt = gson.toJson(json);

View File

@ -3,7 +3,7 @@ package org.hl7.fhir.utilities.npm;
public class CommonPackages {
public static final String ID_XVER = "hl7.fhir.xver-extensions";
public static final String VER_XVER = "0.0.7";
public static final String VER_XVER = "0.0.8";
public static final String ID_PUBPACK = "hl7.fhir.pubpack";
public static final String VER_PUBPACK = "0.0.9";

View File

@ -1476,7 +1476,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private String describeValueSet(String url) {
ValueSet vs = context.fetchResource(ValueSet.class, url);
if (vs != null) {
return "\""+vs.present()+"\" ("+url+")";
return "'"+vs.present()+"' ("+url+")";
} else {
return "("+url+")";
}
@ -1656,6 +1656,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
private StructureDefinition checkExtension(ValidatorHostContext hostContext, List<ValidationMessage> errors, String path, Element resource, Element container, Element element, ElementDefinition def, StructureDefinition profile, NodeStack stack, NodeStack containerStack, String extensionUrl) throws FHIRException {
String url = element.getNamedChildValue("url");
boolean isModifier = element.getName().equals("modifierExtension");
assert def.getIsModifier() == isModifier;
long t = System.nanoTime();
StructureDefinition ex = Utilities.isAbsoluteUrl(url) ? context.fetchResource(StructureDefinition.class, url) : null;
@ -1676,10 +1677,11 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat
}
if (ex != null) {
trackUsage(ex, hostContext, element);
if (def.getIsModifier()) {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", ex.getSnapshot().getElement().get(0).getIsModifier(), I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHY);
// check internal definitions are coherent
if (isModifier) {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", def.getIsModifier() == isModifier, I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHY);
} else {
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", !ex.getSnapshot().getElement().get(0).getIsModifier(), I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHN);
rule(errors, IssueType.STRUCTURE, element.line(), element.col(), path + "[url='" + url + "']", def.getIsModifier() == isModifier, I18nConstants.EXTENSION_EXT_MODIFIER_MISMATCHN);
}
// two questions
// 1. can this extension be used here?

View File

@ -19,7 +19,7 @@
<properties>
<hapi_fhir_version>5.1.0</hapi_fhir_version>
<validator_test_case_version>1.1.74</validator_test_case_version>
<validator_test_case_version>1.1.75</validator_test_case_version>
<junit_jupiter_version>5.7.1</junit_jupiter_version>
<junit_platform_launcher_version>1.7.1</junit_platform_launcher_version>
<maven_surefire_version>3.0.0-M5</maven_surefire_version>