From 58581d5d95bac3751c55ba907683cfb84375eb7f Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 26 Nov 2024 11:48:29 +0000 Subject: [PATCH 01/19] Fix outstanding profile code generation issues --- .../hl7/fhir/r4/profilemodel/PEBuilder.java | 22 +- .../r4/profilemodel/PEDefinitionElement.java | 4 + .../r4/profilemodel/gen/PECodeGenerator.java | 204 +++++++++++------- .../hl7/fhir/r5/profilemodel/PEBuilder.java | 24 ++- .../r5/profilemodel/PEDefinitionElement.java | 4 + .../r5/profilemodel/gen/PECodeGenerator.java | 42 +++- 6 files changed, 206 insertions(+), 94 deletions(-) diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEBuilder.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEBuilder.java index 38e222216..e694e27ee 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEBuilder.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEBuilder.java @@ -352,6 +352,7 @@ public class PEBuilder { list = pu.getChildList(profile, profile.getSnapshot().getElementFirstRep()); } if (list.size() > 0) { + Set names = new HashSet<>(); int i = 0; while (i < list.size()) { ElementDefinition defn = list.get(i); @@ -361,7 +362,8 @@ public class PEBuilder { // DebugUtilities.breakpoint(); i++; } else { - PEDefinitionElement pe = new PEDefinitionElement(this, profile, defn, parent.path()); + String name = uniquefy(names, defn.getName()); + PEDefinitionElement pe = new PEDefinitionElement(this, name, profile, defn, parent.path()); pe.setRecursing(definition == defn || (profile.getDerivation() == TypeDerivationRule.SPECIALIZATION && profile.getType().equals("Extension"))); if (context.isPrimitiveType(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) { pe.setMustHaveValue(definition.getMustHaveValue()); @@ -376,14 +378,14 @@ public class PEBuilder { while (i < list.size() && list.get(i).getPath().equals(defn.getPath())) { StructureDefinition ext = getExtensionDefinition(list.get(i)); if (ext != null) { - res.add(new PEDefinitionExtension(this, list.get(i).getSliceName(), profile, list.get(i), defn, ext, parent.path())); + res.add(new PEDefinitionExtension(this, uniquefy(names, list.get(i).getSliceName()), profile, list.get(i), defn, ext, parent.path())); } else if (isTypeSlicing(defn)) { - res.add(new PEDefinitionTypeSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn, parent.path())); + res.add(new PEDefinitionTypeSlice(this, uniquefy(names, list.get(i).getSliceName()), profile, list.get(i), defn, parent.path())); } else { if (ProfileUtilities.isComplexExtension(profile) && defn.getPath().endsWith(".extension")) { res.add(new PEDefinitionSubExtension(this, profile, list.get(i), parent.path())); } else { - res.add(new PEDefinitionSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn, parent.path())); + res.add(new PEDefinitionSlice(this, uniquefy(names, list.get(i).getSliceName()), profile, list.get(i), defn, parent.path())); } } i++; @@ -409,6 +411,18 @@ public class PEBuilder { } } + private String uniquefy(Set names, String name) { + if (names.contains(name)) { + int i = 0; + while (names.contains(name+i)) { + i++; + } + name = name+i; + } + names.add(name); + return name; + } + protected PEDefinition makeChild(PEDefinition parent, StructureDefinition profileStructure, ElementDefinition definition) { PEDefinitionElement pe = new PEDefinitionElement(this, profileStructure, definition, parent.path()); if (context.isPrimitiveType(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) { diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEDefinitionElement.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEDefinitionElement.java index 766cd44a3..6d700ee29 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEDefinitionElement.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEDefinitionElement.java @@ -42,6 +42,10 @@ public class PEDefinitionElement extends PEDefinition { super(builder, definition.getName(), profile, definition, ppath); } + public PEDefinitionElement(PEBuilder builder, String name, StructureDefinition profile, ElementDefinition definition, String ppath) { + super(builder, name, profile, definition, ppath); + } + @Override public void listTypes(List types) { for (TypeRefComponent t : definition.getType()) { diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java index c6ae413c4..38f55eeb6 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java @@ -56,26 +56,26 @@ import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; /** - * + * * The easiest way to generate code is to use the FHIR Validator, which can generate java classes for profiles * using this code. Parameters: - * + * * -codegen -version r4 -ig hl7.fhir.dk.core#3.2.0 -profiles http://hl7.dk/fhir/core/StructureDefinition/dk-core-gln-identifier,http://hl7.dk/fhir/core/StructureDefinition/dk-core-patient -output /Users/grahamegrieve/temp/codegen -package-name org.hl7.fhir.test - * + * * Parameter Documentation: * -codegen: tells the validator to generate code - * -version {r4|5}: which version to generate for + * -version {r4|5}: which version to generate for * -ig {name}: loads an IG (and it's dependencies) - see -ig documentation for the validator - * -profiles {list}: a comma separated list of profile URLs to generate code for + * -profiles {list}: a comma separated list of profile URLs to generate code for * -output {folder}: the folder where to generate the output java class source code * -package-name {name}: the name of the java package to generate in - * + * * options * -option {name}: a code generation option, one of: - * + * * narrative: generate code for the resource narrative (recommended: don't - leave that for the native resource level) * meta: generate code the what's in meta - * contained: generate code for contained resources + * contained: generate code for contained resources * all-elements: generate code for all elements, not just the key elements (makes the code verbose) */ @@ -87,12 +87,12 @@ public class PECodeGenerator { sdf.setTimeZone(TimeZone.getTimeZone("UTC")); return sdf.format(new Date()); } - - + + public enum ExtensionPolicy { None, Complexes, Primitives; } - + private class PEGenClass { private String name; private String base; @@ -101,7 +101,7 @@ public class PECodeGenerator { private boolean isResource; private Set unfixed = new HashSet<>(); private Set enumNames = new HashSet<>(); - + private StringBuilder inits = new StringBuilder(); private StringBuilder fields = new StringBuilder(); private StringBuilder enums = new StringBuilder(); @@ -114,7 +114,7 @@ public class PECodeGenerator { public void genId() { if (isResource) { genField(true, "id", "String", "id", "", false, "", 0, 1, null); - genAccessors(true, false, "id", "id", "String", "", "String", "String", "Id", "Ids", false, "", false, false, null); + genAccessors(true, false, "id", "id", "String", "", "String", "String", "Id", "Ids", false, "", false, false, null); genLoad(true, false, "id", "id", "id", "IdType", "", "String", "String", "Id", "Ids", false, false, null, false); genSave(true, false, "id", "id", "id", "IdType", "", "String", "String", "Id", "Ids", false, false, false, null, false); genClear(false, "id", "String"); @@ -128,14 +128,14 @@ public class PECodeGenerator { w(b, " */"); w(b); } - w(b, "// Generated by the HAPI Java Profile Generator, "+genDate); - w(b); + w(b, "// Generated by the HAPI Java Profile Generator, "+genDate); + w(b); jdoc(b, doco, 0, true); w(b, "public class "+name+" extends PEGeneratedBase {"); w(b); if (url != null) { w(b, " public static final String CANONICAL_URL = \""+url+"\";"); - w(b); + w(b); } if (enums.length() > 0) { w(b, enums.toString()); @@ -172,7 +172,7 @@ public class PECodeGenerator { w(b, " PEInstance src = builder.buildPEInstance(CANONICAL_URL, source);"); w(b, " theThing.load(src);"); w(b, " return theThing;"); - w(b, " }"); + w(b, " }"); w(b); } else { jdoc(b, "Used when loading other models ", 2, true); @@ -181,13 +181,13 @@ public class PECodeGenerator { w(b, " theThing.workerContext = source.getContext();"); w(b, " theThing.load(source);"); w(b, " return theThing;"); - w(b, " }"); + w(b, " }"); } w(b); w(b, " public void load(PEInstance src) {"); w(b, " clear();"); w(b, load.toString()); - w(b, " }"); + w(b, " }"); w(b); if (isResource) { @@ -201,7 +201,7 @@ public class PECodeGenerator { w(b, " public "+base+" build() {"); w(b, " "+base+" theThing = new "+base+"();"); w(b, " PEBuilder builder = new PEBuilder(workerContext, PEElementPropertiesPolicy.EXTENSION, true);"); - w(b, " PEInstance tgt = builder.buildPEInstance(CANONICAL_URL, theThing);"); + w(b, " PEInstance tgt = builder.buildPEInstance(CANONICAL_URL, theThing);"); w(b, " save(tgt, false);"); w(b, " return theThing;"); w(b, " }"); @@ -217,21 +217,21 @@ public class PECodeGenerator { } w(b, " public void save(PEInstance tgt, boolean nulls) {"); w(b, save.toString()); - w(b, " }"); + w(b, " }"); w(b); if (inits.length() > 0) { w(b, " private void initFixedValues() {"); w(b, inits.toString()); - w(b, " }"); + w(b, " }"); w(b); } w(b, accessors.toString()); w(b); w(b, " public void clear() {"); w(b, clear.toString()); - w(b, " }"); + w(b, " }"); w(b); - w(b, "}"); + w(b, "}"); } private String generateEnum(PEDefinition source, PEDefinition field) { @@ -241,9 +241,14 @@ public class PECodeGenerator { org.hl7.fhir.r4.model.ValueSet vs = workerContext.fetchResource(org.hl7.fhir.r4.model.ValueSet.class, binding.getValueSet()); if (vs != null) { ValueSetExpansionOutcome vse = workerContext.expandVS(vs, false, false); + Set codes = new HashSet<>(); + boolean hasDups = false; if (vse.isOk()) { String baseName = Utilities.nmtokenize(Utilities.singularise(vs.getName())); String name = baseName; + if (workerContext.getResourceNames().contains(name)) { + name = name+"Type"; + } int c = 0; while (enumNames.contains(name)) { c++; @@ -253,26 +258,40 @@ public class PECodeGenerator { for (int i = 0; i < vse.getValueset().getExpansion().getContains().size(); i++) { ValueSetExpansionContainsComponent cc = vse.getValueset().getExpansion().getContains().get(i); String code = Utilities.javaTokenize(cc.getCode(), true).toUpperCase(); + if (Utilities.isInteger(code)) { + code = "C_"+code; + } if (cc.getAbstract()) { code = "_"+code; } + if (codes.contains(code)) { + char sfx = 'A'; + while (codes.contains(code+sfx)) { + sfx++; + } + code = code + sfx; + hasDups = true; + } + codes.add(code); cc.setUserData("java.code", code); w(enums, " "+code+(i < vse.getValueset().getExpansion().getContains().size() - 1 ? "," : ";")+" // \""+cc.getDisplay()+"\" = "+cc.getSystem()+"#"+cc.getCode()); } w(enums, ""); - w(enums, " public static "+name+" fromCode(String s) {"); - w(enums, " switch (s) {"); - for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { - w(enums, " case \""+cc.getCode()+"\": return "+cc.getUserString("java.code")+";"); + if (!hasDups) { + w(enums, " public static "+name+" fromCode(String s) {"); + w(enums, " switch (s) {"); + for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { + w(enums, " case \""+cc.getCode()+"\": return "+cc.getUserString("java.code")+";"); + } + w(enums, " default: return null;"); + w(enums, " }"); + w(enums, " }"); + w(enums, ""); } - w(enums, " default: return null;"); - w(enums, " }"); - w(enums, " }"); - w(enums, ""); w(enums, " public static "+name+" fromCoding(Coding c) {"); for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { if (cc.hasVersion()) { - w(enums, " if (\""+cc.getSystem()+"\".equals(c.getSystem()) && \""+cc.getCode()+"\".equals(c.getCode()) && (!c.hasVersion() || \""+cc.getVersion()+"\".equals(c.getVersion()))) {"); + w(enums, " if (\""+cc.getSystem()+"\".equals(c.getSystem()) && \""+cc.getCode()+"\".equals(c.getCode()) && (!c.hasVersion() || \""+cc.getVersion()+"\".equals(c.getVersion()))) {"); } else { w(enums, " if (\""+cc.getSystem()+"\".equals(c.getSystem()) && \""+cc.getCode()+"\".equals(c.getCode())) {"); } @@ -302,16 +321,18 @@ public class PECodeGenerator { w(enums, " }"); w(enums, " }"); w(enums, ""); - - w(enums, " public String toCode() {"); - w(enums, " switch (this) {"); - for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { - w(enums, " case "+cc.getUserString("java.code")+": return \""+cc.getCode()+"\";"); + + if (!hasDups) { + w(enums, " public String toCode() {"); + w(enums, " switch (this) {"); + for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { + w(enums, " case "+cc.getUserString("java.code")+": return \""+cc.getCode()+"\";"); + } + w(enums, " default: return null;"); + w(enums, " }"); + w(enums, " }"); + w(enums, ""); } - w(enums, " default: return null;"); - w(enums, " }"); - w(enums, " }"); - w(enums, ""); w(enums, " public Coding toCoding() {"); w(enums, " switch (this) {"); for (ValueSetExpansionContainsComponent cc : vse.getValueset().getExpansion().getContains()) { @@ -337,6 +358,7 @@ public class PECodeGenerator { } return null; } + private void defineField(PEDefinition source, PEDefinition field) { if (field.types().size() == 1) { StructureDefinition sd = workerContext.fetchTypeDefinition(field.types().get(0).getUrl()); @@ -377,8 +399,8 @@ public class PECodeGenerator { if (isPrim && field.hasFixedValue()) { genFixed(name, ptype, field.getFixedValue()); } - genAccessors(isPrim, isAbstract, name, field.name(), type, init, ptype, ltype, cname, csname, field.isList(), field.documentation(), field.hasFixedValue(), isEnum, field.definition()); - genLoad(isPrim, isAbstract, name, sname, field.name(), type, init, ptype, ltype, cname, csname, field.isList(), field.hasFixedValue(), field.types().get(0), isEnum); + genAccessors(isPrim, isAbstract, name, field.name(), type, init, ptype, ltype, cname, csname, field.isList(), field.documentation(), field.hasFixedValue(), isEnum, field.definition()); + genLoad(isPrim, isAbstract, name, sname, field.name(), type, init, ptype, ltype, cname, csname, field.isList(), field.hasFixedValue(), field.types().get(0), isEnum); genSave(isPrim, isAbstract, name, sname, field.name(), type, init, ptype, ltype, cname, csname, field.isList(), field.hasFixedValue(), isExtension, field.types().get(0), isEnum); genClear(field.isList(), name, ptype); } @@ -386,10 +408,10 @@ public class PECodeGenerator { // ignoring polymorphics for now } } - + private void genClear(boolean list, String name, String ptype) { if (list) { - w(clear, " "+name+".clear();"); + w(clear, " "+name+".clear();"); } else if ("boolean".equals(ptype)) { w(clear, " "+name+" = false;"); } else if ("int".equals(ptype)) { @@ -398,16 +420,25 @@ public class PECodeGenerator { w(clear, " "+name+" = null;"); } } - + private void genLoad(boolean isPrim, boolean isAbstract, String name, String sname, String fname, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, boolean isFixed, PEType typeInfo, boolean isEnum) { if (isList) { w(load, " for (PEInstance item : src.children(\""+fname+"\")) {"); - if ("BackboneElement".equals(type)) { - w(load, " "+name+".add(("+type+") item.asElement());"); - } else if (!Strings.isNullOrEmpty(typeInfo.getUrl()) && typeInfo.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition")) { - w(load, " "+name+".add(("+type+") item.asDataType());"); + if (typeInfo != null && typeInfo.getUrl() != null && !typeInfo.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition")) { + w(load, " "+name+".add("+type+".fromSource(src.child(\""+fname+"\")));"); + + } else if ("BackboneElement".equals(type)) { + w(load, " "+name+".add(("+type+") item.asElement());"); + } else if (isEnum) { + if ("CodeableConcept".equals(typeInfo.getName())) { + w(load, " "+name+".add("+type+".fromCodeableConcept((CodeableConcept) item.asDataType()));"); + } else if ("Coding".equals(typeInfo.getName())) { + w(load, " "+name+".add("+type+".fromCoding((Coding) item.asDataType()));"); + } else { + w(load, " "+name+".add("+type+".fromCode(item.asDataType().primitiveValue()));"); + } } else { - w(load, " "+name+".add("+type+".fromSource(item));"); + w(load, " "+name+".add(("+type+") item.asDataType());"); } w(load, " }"); } else if (isEnum) { @@ -418,23 +449,23 @@ public class PECodeGenerator { w(load, " "+name+" = "+type+".fromCoding((Coding) src.child(\""+fname+"\").asDataType());"); } else { w(load, " "+name+" = "+type+".fromCode(src.child(\""+fname+"\").asDataType().primitiveValue());"); - } - w(load, " }"); + } + w(load, " }"); } else if (isPrim) { w(load, " if (src.hasChild(\""+fname+"\")) {"); if ("CodeType".equals(type)) { - // might be code or enum + // might be code or enum w(load, " "+name+" = src.child(\""+fname+"\").asDataType().primitiveValue();"); } else { w(load, " "+name+" = (("+type+") src.child(\""+fname+"\").asDataType()).getValue();"); } - w(load, " }"); + w(load, " }"); } else if (typeInfo != null && typeInfo.getUrl() != null && !typeInfo.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition")) { w(load, " if (src.hasChild(\""+fname+"\")) {"); w(load, " "+name+" = "+type+".fromSource(src.child(\""+fname+"\"));"); w(load, " }"); } else { - w(load, " if (src.hasChild(\""+fname+"\")) {"); + w(load, " if (src.hasChild(\""+fname+"\")) {"); if ("BackboneElement".equals(type)) { w(load, " "+name+" = ("+type+") src.child(\""+fname+"\").asElement();"); } else if (Utilities.existsInList(type, workerContext.getResourceNames())) { @@ -450,10 +481,20 @@ public class PECodeGenerator { w(save, " tgt.clear(\""+fname+"\");"); if (isList) { w(save, " for ("+type+" item : "+name+") {"); - if (isExtension && !Strings.isNullOrEmpty(typeInfo.getUrl()) && typeInfo.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition")) { - w(save, " tgt.makeChild(\""+fname+"\").data().setProperty(\"value[x]\", item);"); - } else if (isExtension) { - w(save, " tgt.makeChild(\""+fname+"\").data().setProperty(\"value[x]\", item.getData());"); + if (isExtension) { + if (typeInfo != null && typeInfo.getUrl() != null && !typeInfo.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition")) { + w(save, " item.save(tgt.makeChild(\""+fname+"\"), false);"); + } else { + w(save, " tgt.makeChild(\""+fname+"\").data().setProperty(\"value[x]\", item);"); + } + } else if (isEnum) { + if ("CodeableConcept".equals(typeInfo.getName())) { + w(save, " tgt.addChild(\""+fname+"\", item.toCodeableConcept());"); + } else if ("Coding".equals(typeInfo.getName())) { + w(save, " tgt.addChild(\""+fname+"\", item.toCoding());"); + } else { + w(save, " tgt.addChild(\""+fname+"\", item.toCode());"); + } } else { w(save, " tgt.addChild(\""+fname+"\", item);"); } @@ -466,8 +507,8 @@ public class PECodeGenerator { w(save, " tgt.addChild(\""+fname+"\", "+name+".toCoding());"); } else { w(save, " tgt.addChild(\""+fname+"\", "+name+".toCode());"); - } - w(save, " }"); + } + w(save, " }"); } else if (isPrim) { if ("boolean".equals(ptype)) { w(save, " if (true) { // for now, at least"); @@ -479,7 +520,7 @@ public class PECodeGenerator { if (isExtension) { w(save, " tgt.makeChild(\""+fname+"\").data().setProperty(\"value[x]\", new "+type+"("+name+"));"); } else if (Utilities.existsInList(type, "DateType", "InstantType", "DateTimeType")) { - w(save, " tgt.addChild(\""+fname+"\", new "+type+"("+name+"));"); + w(save, " tgt.addChild(\""+fname+"\", new "+type+"("+name+"));"); } else { w(save, " tgt.makeChild(\""+fname+"\").data().setProperty(\"value\", new "+type+"("+name+"));"); } @@ -511,7 +552,7 @@ public class PECodeGenerator { if (isFixed) { w(accessors, " public boolean has"+cname+"() {"); w(accessors, " return true;"); - w(accessors, " }"); + w(accessors, " }"); } else { w(accessors, " public "+this.name+" set"+cname+"("+ptype+" value) {"); w(accessors, " this."+name+" = value;"); @@ -520,13 +561,13 @@ public class PECodeGenerator { w(accessors); w(accessors, " public boolean has"+cname+"() {"); if ("boolean".equals(ptype)) { - w(accessors, " return true; // not "+name+" != false ?"); + w(accessors, " return true; // not "+name+" != false ?"); } else if ("int".equals(ptype)) { - w(accessors, " return "+name+" != 0;"); + w(accessors, " return "+name+" != 0;"); } else { w(accessors, " return "+name+" != null;"); } - w(accessors, " }"); + w(accessors, " }"); } } else { if (isPrim && !isList) { @@ -553,24 +594,24 @@ public class PECodeGenerator { w(accessors, " return "+name+" != null && !"+name+".isEmpty();"); w(accessors, " }"); w(accessors); - if (!isAbstract) { + if (!isAbstract && !isEnum) { w(accessors, " public "+type+" add"+csname+"() {"); w(accessors, " "+type+" theThing = new "+type+"();"); w(accessors, " get"+cname+"().add(theThing);"); w(accessors, " return theThing;"); w(accessors, " }"); - w(accessors); + w(accessors); } w(accessors, " public boolean has"+csname+"("+type+" item) {"); w(accessors, " return has"+cname+"() && "+name+".contains(item);"); w(accessors, " }"); - w(accessors); + w(accessors); w(accessors, " public void remove"+csname+"("+type+" item) {"); w(accessors, " if (has"+csname+"(item)) {"); w(accessors, " "+name+".remove(item);"); w(accessors, " }"); w(accessors, " }"); - w(accessors); + w(accessors); } else if (isPrim) { if (!isFixed) { w(accessors, " public "+this.name+" set"+cname+"("+ptype+" value) {"); @@ -586,7 +627,7 @@ public class PECodeGenerator { w(accessors, " public boolean has"+cname+"() {"); w(accessors, " return "+name+" != null && "+name+".hasValue();"); w(accessors, " }"); - w(accessors); + w(accessors); } else { if (!isFixed) { w(accessors, " public "+this.name+" set"+cname+"("+type+" value) {"); @@ -610,7 +651,7 @@ public class PECodeGenerator { w(fields, " @BindingStrength(\""+ed.getBinding().getStrength().toCode()+"\") @ValueSet(\""+ed.getBinding().getValueSet()+"\")"); } if (ed.getMustSupport()) { - w(fields, " @MustSupport(true)"); + w(fields, " @MustSupport(true)"); } if (ed.hasLabel() || ed.hasDefinition()) { String s = ""; @@ -620,7 +661,7 @@ public class PECodeGenerator { if (ed.hasDefinition()) { s = s + " @Definition(\""+Utilities.escapeJava(ed.getDefinition())+"\")"; } - w(fields, " "+s); + w(fields, " "+s); } } if (isPrim && extensionPolicy != ExtensionPolicy.Primitives && !isList) { @@ -628,7 +669,7 @@ public class PECodeGenerator { } else if (isList) { w(fields, " private "+ltype+" "+name+" = new ArrayList<>();"+nn+" // "+shortDoco); } else { - w(fields, " private "+ltype+" "+name+";"+nn+" // "+shortDoco); + w(fields, " private "+ltype+" "+name+";"+nn+" // "+shortDoco); } w(fields, ""); } @@ -768,8 +809,9 @@ public class PECodeGenerator { PEDefinition source = new PEBuilder(workerContext, PEElementPropertiesPolicy.EXTENSION, true).buildPEDefinition(canonical); w(imports, "import java.util.List;"); w(imports, "import java.util.ArrayList;"); - w(imports, "import javax.annotation.Nullable;"); w(imports, "import java.util.Date;\r\n"); + w(imports, "import java.math.BigDecimal;"); + w(imports, "import javax.annotation.Nullable;"); w(imports); w(imports, "import org.hl7.fhir."+version+".context.IWorkerContext;"); w(imports, "import org.hl7.fhir."+version+".model.*;"); @@ -785,7 +827,7 @@ public class PECodeGenerator { w(imports, "import org.hl7.fhir."+version+".profilemodel.gen.ValueSet;"); w(imports, "import org.hl7.fhir."+version+".profilemodel.gen.MustSupport;"); w(imports, "import org.hl7.fhir."+version+".profilemodel.gen.Definition;"); - + PEGenClass cls = genClass(source); StringBuilder b = new StringBuilder(); @@ -810,7 +852,7 @@ public class PECodeGenerator { w(b, pfx+" *"); } w(b, pfx+" */"); - } + } } private List naturalLines(String line) { @@ -839,7 +881,7 @@ public class PECodeGenerator { private void w(StringBuilder b, String line) { b.append(line); - w(b); + w(b); } private PEGenClass genClass(PEDefinition source) { @@ -854,7 +896,7 @@ public class PECodeGenerator { if (genForField(source, child)) { cls.defineField(source, child); } - } + } return cls; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java index ad465acbd..dd36b5be9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEBuilder.java @@ -29,7 +29,9 @@ package org.hl7.fhir.r5.profilemodel; */ import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.apache.commons.lang3.NotImplementedException; import org.hl7.fhir.exceptions.DefinitionException; @@ -352,6 +354,7 @@ public class PEBuilder { list = pu.getChildList(profile, profile.getSnapshot().getElementFirstRep()); } if (list.size() > 0) { + Set names = new HashSet<>(); int i = 0; while (i < list.size()) { ElementDefinition defn = list.get(i); @@ -361,7 +364,8 @@ public class PEBuilder { // DebugUtilities.breakpoint(); i++; } else { - PEDefinitionElement pe = new PEDefinitionElement(this, profile, defn, parent.path()); + String name = uniquefy(names, defn.getName()); + PEDefinitionElement pe = new PEDefinitionElement(this, name, profile, defn, parent.path()); pe.setRecursing(definition == defn || (profile.getDerivation() == TypeDerivationRule.SPECIALIZATION && profile.getType().equals("Extension"))); if (context.isPrimitiveType(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) { pe.setMustHaveValue(definition.getMustHaveValue()); @@ -376,14 +380,14 @@ public class PEBuilder { while (i < list.size() && list.get(i).getPath().equals(defn.getPath())) { StructureDefinition ext = getExtensionDefinition(list.get(i)); if (ext != null) { - res.add(new PEDefinitionExtension(this, list.get(i).getSliceName(), profile, list.get(i), defn, ext, parent.path())); + res.add(new PEDefinitionExtension(this, uniquefy(names, list.get(i).getSliceName()), profile, list.get(i), defn, ext, parent.path())); } else if (isTypeSlicing(defn)) { - res.add(new PEDefinitionTypeSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn, parent.path())); + res.add(new PEDefinitionTypeSlice(this, uniquefy(names, list.get(i).getSliceName()), profile, list.get(i), defn, parent.path())); } else { if (ProfileUtilities.isComplexExtension(profile) && defn.getPath().endsWith(".extension")) { res.add(new PEDefinitionSubExtension(this, profile, list.get(i), parent.path())); } else { - res.add(new PEDefinitionSlice(this, list.get(i).getSliceName(), profile, list.get(i), defn, parent.path())); + res.add(new PEDefinitionSlice(this, uniquefy(names, list.get(i).getSliceName()), profile, list.get(i), defn, parent.path())); } } i++; @@ -409,6 +413,18 @@ public class PEBuilder { } } + private String uniquefy(Set names, String name) { + if (names.contains(name)) { + int i = 0; + while (names.contains(name+i)) { + i++; + } + name = name+i; + } + names.add(name); + return name; + } + protected PEDefinition makeChild(PEDefinition parent, StructureDefinition profileStructure, ElementDefinition definition) { PEDefinitionElement pe = new PEDefinitionElement(this, profileStructure, definition, parent.path()); if (context.isPrimitiveType(definition.getTypeFirstRep().getWorkingCode()) && "value".equals(pe.name())) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionElement.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionElement.java index 916b0928c..cb6be83ac 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionElement.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/PEDefinitionElement.java @@ -42,6 +42,10 @@ public class PEDefinitionElement extends PEDefinition { super(builder, definition.getName(), profile, definition, ppath); } + public PEDefinitionElement(PEBuilder builder, String name, StructureDefinition profile, ElementDefinition definition, String ppath) { + super(builder, name, profile, definition, ppath); + } + @Override public void listTypes(List types) { for (TypeRefComponent t : definition.getType()) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java index d7ab86c29..c280c32e5 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java @@ -58,6 +58,7 @@ import org.hl7.fhir.r5.profilemodel.PEDefinition; import org.hl7.fhir.r5.profilemodel.PEInstance; import org.hl7.fhir.r5.profilemodel.PEType; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; +import org.hl7.fhir.utilities.DebugUtilities; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; @@ -252,6 +253,9 @@ public class PECodeGenerator { if (vse.isOk()) { String baseName = Utilities.nmtokenize(Utilities.singularise(vs.getName())); String name = baseName; + if (workerContext.getResourceNames().contains(name)) { + name = name+"Type"; + } int c = 0; while (enumNames.contains(name)) { c++; @@ -427,8 +431,19 @@ public class PECodeGenerator { private void genLoad(boolean isPrim, boolean isAbstract, String name, String sname, String fname, String type, String init, String ptype, String ltype, String cname, String csname, boolean isList, boolean isFixed, PEType typeInfo, boolean isEnum) { if (isList) { w(load, " for (PEInstance item : src.children(\""+fname+"\")) {"); - if ("BackboneElement".equals(type)) { + if (typeInfo != null && typeInfo.getUrl() != null && !typeInfo.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition")) { + w(load, " "+name+".add("+type+".fromSource(src.child(\""+fname+"\")));"); + + } else if ("BackboneElement".equals(type)) { w(load, " "+name+".add(("+type+") item.asElement());"); + } else if (isEnum) { + if ("CodeableConcept".equals(typeInfo.getName())) { + w(load, " "+name+".add("+type+".fromCodeableConcept((CodeableConcept) item.asDataType()));"); + } else if ("Coding".equals(typeInfo.getName())) { + w(load, " "+name+".add("+type+".fromCoding((Coding) item.asDataType()));"); + } else { + w(load, " "+name+".add("+type+".fromCode(item.asDataType().primitiveValue()));"); + } } else { w(load, " "+name+".add(("+type+") item.asDataType());"); } @@ -474,7 +489,19 @@ public class PECodeGenerator { if (isList) { w(save, " for ("+type+" item : "+name+") {"); if (isExtension) { - w(save, " tgt.makeChild(\""+fname+"\").data().setProperty(\"value[x]\", item);"); + if (typeInfo != null && typeInfo.getUrl() != null && !typeInfo.getUrl().startsWith("http://hl7.org/fhir/StructureDefinition")) { + w(save, " item.save(tgt.makeChild(\""+fname+"\"), false);"); + } else { + w(save, " tgt.makeChild(\""+fname+"\").data().setProperty(\"value[x]\", item);"); + } + } else if (isEnum) { + if ("CodeableConcept".equals(typeInfo.getName())) { + w(save, " tgt.addChild(\""+fname+"\", item.toCodeableConcept());"); + } else if ("Coding".equals(typeInfo.getName())) { + w(save, " tgt.addChild(\""+fname+"\", item.toCoding());"); + } else { + w(save, " tgt.addChild(\""+fname+"\", item.toCode());"); + } } else { w(save, " tgt.addChild(\""+fname+"\", item);"); } @@ -574,7 +601,7 @@ public class PECodeGenerator { w(accessors, " return "+name+" != null && !"+name+".isEmpty();"); w(accessors, " }"); w(accessors); - if (!isAbstract) { + if (!isAbstract && !isEnum) { w(accessors, " public "+type+" add"+csname+"() {"); w(accessors, " "+type+" theThing = new "+type+"();"); w(accessors, " get"+cname+"().add(theThing);"); @@ -786,11 +813,16 @@ public class PECodeGenerator { public String execute() throws IOException { imports = new StringBuilder(); + if ("http://ehealth.sundhed.dk/fhir/StructureDefinition/ehealth-appointment".equals(canonical)) { + DebugUtilities.breakpoint(); + } + PEDefinition source = new PEBuilder(workerContext, PEElementPropertiesPolicy.EXTENSION, true).buildPEDefinition(canonical); w(imports, "import java.util.List;"); w(imports, "import java.util.ArrayList;"); - w(imports, "import javax.annotation.Nullable;"); w(imports, "import java.util.Date;\r\n"); + w(imports, "import java.math.BigDecimal;"); + w(imports, "import javax.annotation.Nullable;"); w(imports); w(imports, "import org.hl7.fhir."+version+".context.IWorkerContext;"); w(imports, "import org.hl7.fhir."+version+".model.*;"); @@ -807,7 +839,7 @@ public class PECodeGenerator { w(imports, "import org.hl7.fhir."+version+".profilemodel.gen.MustSupport;"); w(imports, "import org.hl7.fhir."+version+".profilemodel.gen.Definition;"); - + PEGenClass cls = genClass(source); StringBuilder b = new StringBuilder(); w(b, "package "+pkgName+";"); From 93f74f968d25ab76dab2b61050b0eb7dfc3dffd3 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 26 Nov 2024 11:48:42 +0000 Subject: [PATCH 02/19] fix version issues in snapshot generation tests --- .../convertors/loaders/XVersionLoader.java | 50 +++++++++++++++++++ .../tests/SnapShotGenerationXTests.java | 17 +++++-- 2 files changed, 64 insertions(+), 3 deletions(-) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/XVersionLoader.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/XVersionLoader.java index a811f0de1..e5df003d6 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/XVersionLoader.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/XVersionLoader.java @@ -2,6 +2,7 @@ package org.hl7.fhir.convertors.loaders; import java.io.IOException; import java.io.InputStream; +import java.io.OutputStream; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_50; @@ -9,6 +10,7 @@ import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.formats.IParser.OutputStyle; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; @@ -53,4 +55,52 @@ public class XVersionLoader { throw new FHIRException("Unknown version " + version + " loading resource"); } + public static void saveXml(String version, Resource resource, OutputStream stream) throws FHIRFormatError, IOException { + if (Utilities.noString(version)) { + new org.hl7.fhir.r5.formats.XmlParser().compose(stream, resource, true); + } + switch (VersionUtilities.getMajMin(version)) { + case "1.0": + new org.hl7.fhir.dstu2.formats.XmlParser().compose(stream, VersionConvertorFactory_10_50.convertResource(resource), true); + return; + case "1.4": + new org.hl7.fhir.dstu2016may.formats.XmlParser().compose(stream, VersionConvertorFactory_14_50.convertResource(resource), true); + return; + case "3.0": + new org.hl7.fhir.dstu3.formats.XmlParser().compose(stream, VersionConvertorFactory_30_50.convertResource(resource), true); + return; + case "4.0": + new org.hl7.fhir.r4.formats.XmlParser().compose(stream, VersionConvertorFactory_40_50.convertResource(resource), true); + return; + case "5.0": + new org.hl7.fhir.r5.formats.XmlParser().compose(stream, resource, true); + return; + } + throw new FHIRException("Unknown version " + version + " loading resource"); + } + + public static void saveJson(String version, Resource resource, OutputStream stream) throws FHIRException, IOException { + if (Utilities.noString(version)) { + new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(stream, resource); + } + switch (VersionUtilities.getMajMin(version)) { + case "1.0": + new org.hl7.fhir.dstu2.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu2.formats.IParser.OutputStyle.PRETTY).compose(stream, VersionConvertorFactory_10_50.convertResource(resource)); + return; + case "1.4": + new org.hl7.fhir.dstu2016may.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu2016may.formats.IParser.OutputStyle.PRETTY).compose(stream, VersionConvertorFactory_14_50.convertResource(resource)); + return; + case "3.0": + new org.hl7.fhir.dstu3.formats.JsonParser().setOutputStyle(org.hl7.fhir.dstu3.formats.IParser.OutputStyle.PRETTY).compose(stream, VersionConvertorFactory_30_50.convertResource(resource)); + return; + case "4.0": + new org.hl7.fhir.r4.formats.JsonParser().setOutputStyle(org.hl7.fhir.r4.formats.IParser.OutputStyle.PRETTY).compose(stream, VersionConvertorFactory_40_50.convertResource(resource)); + return; + case "5.0": + new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(org.hl7.fhir.r5.formats.IParser.OutputStyle.PRETTY).compose(stream, resource); + return; + } + throw new FHIRException("Unknown version " + version + " loading resource"); + } + } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java index cae74e6ce..9f4d5cc56 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java @@ -25,6 +25,7 @@ import org.hl7.fhir.r5.fhirpath.ExpressionNode.CollectionStatus; import org.hl7.fhir.r5.fhirpath.FHIRPathEngine.IEvaluationContext; import org.hl7.fhir.r5.fhirpath.FHIRPathUtilityClasses.FunctionDetails; import org.hl7.fhir.r5.formats.IParser.OutputStyle; +import org.hl7.fhir.r5.formats.JsonParser; import org.hl7.fhir.r5.formats.XmlParser; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; @@ -103,6 +104,7 @@ public class SnapShotGenerationXTests { private StructureDefinition included; private StructureDefinition expected; private StructureDefinition output; + public boolean outputIsJson; public TestDetails(Element test) { super(); @@ -181,8 +183,13 @@ public class SnapShotGenerationXTests { source = (StructureDefinition) XVersionLoader.loadJson(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-input.json")); else source = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-input.xml")); - if (!fail) - expected = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-output.xml")); + if (!fail) { + if (TestingUtilities.findTestResource("rX", "snapshot-generation", id + "-output.json")) { + outputIsJson = true; + expected = (StructureDefinition) XVersionLoader.loadJson(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-output.json")); + } else + expected = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-output.xml")); + } if (!Utilities.noString(include)) included = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", include + ".xml")); if (!Utilities.noString(register)) { @@ -556,7 +563,11 @@ public class SnapShotGenerationXTests { File dst = ManagedFileAccess.file(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output.xml")); if (dst.exists()) dst.delete(); - new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output.xml")), output); + if (test.outputIsJson) { + XVersionLoader.saveJson(version, output, ManagedFileAccess.outStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output.json"))); + } else { + XVersionLoader.saveXml(version, output, ManagedFileAccess.outStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output.json"))); + } StructureDefinition t1 = test.expected.copy(); t1.setText(null); StructureDefinition t2 = test.output.copy(); From c2e197a65d9bb19b515d9f7a45866ecf8cdb0b46 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 26 Nov 2024 11:49:00 +0000 Subject: [PATCH 03/19] Fix filter comparison logic when comparing valuesets --- .../java/org/hl7/fhir/r5/comparison/ValueSetComparer.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java index 9fb1fe6fa..3594ac32f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java @@ -165,7 +165,7 @@ public class ValueSetComparer extends CanonicalResourceComparer { intersection.getInclude().add(csI); StructuralMatch sm = new StructuralMatch(l, r); res.getIncludes().getChildren().add(sm); - def = compareDefinitions("ValueSet.compose.exclude["+right.getInclude().indexOf(r)+"]", l, r, sm, csM, csI, res) || def; + def = compareDefinitions("ValueSet.compose.include["+right.getInclude().indexOf(r)+"]", l, r, sm, csM, csI, res) || def; } } for (ConceptSetComponent r : right.getInclude()) { @@ -341,7 +341,7 @@ public class ValueSetComparer extends CanonicalResourceComparer { intersection.getFilter().add(ci); StructuralMatch sm = new StructuralMatch(l, r); combined.getChildren().add(sm); - if (!compareFilters(l, r, sm, cu, ci)) { + if (compareFilters(l, r, sm, cu, ci)) { res.updateContentState(true); session.markChanged(r, l); } @@ -420,7 +420,7 @@ public class ValueSetComparer extends CanonicalResourceComparer { cu.setOp(l.getOp()); cu.setValue(l.getValue()); } - return !l.getProperty().equals(r.getProperty()); + return !l.getValue().equals(r.getValue()); } private CanonicalType findInList(List matches, CanonicalType item, List source) { From 597c4ecc886b7c60ce39c5a9800b173ef993865e Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 26 Nov 2024 11:51:59 +0000 Subject: [PATCH 04/19] compile fix --- .../src/main/java/org/hl7/fhir/r4/profilemodel/PEBuilder.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEBuilder.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEBuilder.java index e694e27ee..54f953a77 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEBuilder.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/PEBuilder.java @@ -29,7 +29,9 @@ package org.hl7.fhir.r4.profilemodel; */ import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; import org.apache.commons.lang3.NotImplementedException; import org.hl7.fhir.exceptions.DefinitionException; From 4be38a5e0c0ede664cd77a51bb76da74fecc60ed Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 26 Nov 2024 12:12:36 +0000 Subject: [PATCH 05/19] more compile fixes --- .../org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java index c280c32e5..7d332b0df 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java @@ -58,7 +58,6 @@ import org.hl7.fhir.r5.profilemodel.PEDefinition; import org.hl7.fhir.r5.profilemodel.PEInstance; import org.hl7.fhir.r5.profilemodel.PEType; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; -import org.hl7.fhir.utilities.DebugUtilities; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; @@ -813,10 +812,6 @@ public class PECodeGenerator { public String execute() throws IOException { imports = new StringBuilder(); - if ("http://ehealth.sundhed.dk/fhir/StructureDefinition/ehealth-appointment".equals(canonical)) { - DebugUtilities.breakpoint(); - } - PEDefinition source = new PEBuilder(workerContext, PEElementPropertiesPolicy.EXTENSION, true).buildPEDefinition(canonical); w(imports, "import java.util.List;"); w(imports, "import java.util.ArrayList;"); From 17b22424f6bc75a929ed0871f0befd43a90c1f92 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Wed, 27 Nov 2024 04:44:19 +0000 Subject: [PATCH 06/19] fix missing port from server when doing tx-registry redirections --- .../client/TerminologyClientManager.java | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java index 9f1d4958d..5add4914a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java @@ -320,15 +320,24 @@ public class TerminologyClientManager { serverList = decideWhichServer(s); // testing support try { - serverList.replace("tx.fhir.org", new URL(getMasterClient().getAddress()).getHost()); + serverList.replace("tx.fhir.org", host()); } catch (MalformedURLException e) { } - resMap.put(s, serverList); + // resMap.put(s, serverList); save(); } return serverList; } + private String host() throws MalformedURLException { + URL url = new URL(getMasterClient().getAddress()); + if (url.getPort() != 0) { + return url.getHost()+":"+url.getPort(); + } else { + return url.getHost(); + } + } + private ServerOptionList decideWhichServer(String url) { if (IGNORE_TX_REGISTRY || !useEcosystem) { return new ServerOptionList(getMasterClient().getAddress()); @@ -545,7 +554,7 @@ public class TerminologyClientManager { } if (server.contains("://tx.fhir.org")) { try { - server = server.replace("tx.fhir.org", new URL(getMasterClient().getAddress()).getHost()); + server = server.replace("tx.fhir.org", host()); } catch (MalformedURLException e) { } } @@ -627,7 +636,7 @@ public class TerminologyClientManager { } if (server.contains("://tx.fhir.org")) { try { - server = server.replace("tx.fhir.org", new URL(getMasterClient().getAddress()).getHost()); + server = server.replace("tx.fhir.org", host()); } catch (MalformedURLException e) { } } From 69b96d8d3e5d372c6799bef5930b3e84df7db3e5 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Fri, 29 Nov 2024 11:42:50 +0000 Subject: [PATCH 07/19] generate enum add routine --- .../r4/profilemodel/gen/PECodeGenerator.java | 21 ++++++++++++------- .../r5/profilemodel/gen/PECodeGenerator.java | 21 ++++++++++++------- 2 files changed, 28 insertions(+), 14 deletions(-) diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java index 38f55eeb6..f2c61b840 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java @@ -594,13 +594,20 @@ public class PECodeGenerator { w(accessors, " return "+name+" != null && !"+name+".isEmpty();"); w(accessors, " }"); w(accessors); - if (!isAbstract && !isEnum) { - w(accessors, " public "+type+" add"+csname+"() {"); - w(accessors, " "+type+" theThing = new "+type+"();"); - w(accessors, " get"+cname+"().add(theThing);"); - w(accessors, " return theThing;"); - w(accessors, " }"); - w(accessors); + if (!isAbstract) { + if (!isEnum) { + w(accessors, " public "+type+" add"+csname+"() {"); + w(accessors, " "+type+" theThing = new "+type+"();"); + w(accessors, " get"+cname+"().add(theThing);"); + w(accessors, " return theThing;"); + w(accessors, " }"); + w(accessors); + } else { + w(accessors, " public void add"+csname+"("+type+" theThing) {"); + w(accessors, " get"+cname+"().add(theThing);"); + w(accessors, " }"); + w(accessors); + } } w(accessors, " public boolean has"+csname+"("+type+" item) {"); w(accessors, " return has"+cname+"() && "+name+".contains(item);"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java index 7d332b0df..4c686dabf 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java @@ -600,13 +600,20 @@ public class PECodeGenerator { w(accessors, " return "+name+" != null && !"+name+".isEmpty();"); w(accessors, " }"); w(accessors); - if (!isAbstract && !isEnum) { - w(accessors, " public "+type+" add"+csname+"() {"); - w(accessors, " "+type+" theThing = new "+type+"();"); - w(accessors, " get"+cname+"().add(theThing);"); - w(accessors, " return theThing;"); - w(accessors, " }"); - w(accessors); + if (!isAbstract) { + if (!isEnum) { + w(accessors, " public "+type+" add"+csname+"() {"); + w(accessors, " "+type+" theThing = new "+type+"();"); + w(accessors, " get"+cname+"().add(theThing);"); + w(accessors, " return theThing;"); + w(accessors, " }"); + w(accessors); + } else { + w(accessors, " public void add"+csname+"("+type+" theThing) {"); + w(accessors, " get"+cname+"().add(theThing);"); + w(accessors, " }"); + w(accessors); + } } w(accessors, " public boolean has"+csname+"("+type+" item) {"); w(accessors, " return has"+cname+"() && "+name+".contains(item);"); From 3adbd0020956a09362e65c50dbc3a33f96f0f71a Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Dec 2024 06:47:12 +0300 Subject: [PATCH 08/19] Fix presentation issues and union and intersection links in previous version comparison --- .../r5/comparison/CodeSystemComparer.java | 22 +++++++++++ .../r5/comparison/ComparisonRenderer.java | 39 +++++++++++++++++-- .../fhir/r5/comparison/ResourceComparer.java | 2 +- .../fhir/r5/comparison/ValueSetComparer.java | 21 ++++++++++ 4 files changed, 79 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CodeSystemComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CodeSystemComparer.java index 38cbf1068..9c7914bbb 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CodeSystemComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/CodeSystemComparer.java @@ -9,6 +9,8 @@ import java.util.Map; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.comparison.StructureDefinitionComparer.ProfileComparison; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.CodeSystem; import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent; @@ -16,16 +18,26 @@ 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.CodeSystem.PropertyComponent; +import org.hl7.fhir.r5.renderers.StructureDefinitionRenderer; +import org.hl7.fhir.r5.renderers.CodeSystemRenderer; +import org.hl7.fhir.r5.renderers.Renderer.RenderingStatus; +import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; +import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; +import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.RenderingI18nContext; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; +import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; +import org.hl7.fhir.utilities.xhtml.XhtmlDocument; import org.hl7.fhir.utilities.xhtml.XhtmlNode; public class CodeSystemComparer extends CanonicalResourceComparer { @@ -628,4 +640,14 @@ public class CodeSystemComparer extends CanonicalResourceComparer { return "CodeSystem"; } + public XhtmlNode renderUnion(CodeSystemComparison comp, String id, String prefix, String corePath) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome { + CodeSystemRenderer csr = new CodeSystemRenderer(new RenderingContext(session.getContextLeft(), null, new ValidationOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER)); + return csr.buildNarrative(ResourceWrapper.forResource(csr.getContext(), comp.union)); + } + + public XhtmlNode renderIntersection(CodeSystemComparison comp, String id, String prefix, String corePath) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome { + CodeSystemRenderer csr = new CodeSystemRenderer(new RenderingContext(session.getContextLeft(), null, new ValidationOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER)); + return csr.buildNarrative(ResourceWrapper.forResource(csr.getContext(), comp.intersection)); + } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java index 8583c96e4..b9d29fae0 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ComparisonRenderer.java @@ -12,7 +12,9 @@ import java.util.List; import java.util.Map; import java.util.Set; +import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; import org.hl7.fhir.exceptions.PathEngineException; import org.hl7.fhir.r5.comparison.CapabilityStatementComparer.CapabilityStatementComparison; import org.hl7.fhir.r5.comparison.CodeSystemComparer.CodeSystemComparison; @@ -32,6 +34,7 @@ import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.Tuple; import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.r5.utils.LiquidEngine; import org.hl7.fhir.r5.utils.LiquidEngine.LiquidDocument; import org.hl7.fhir.utilities.FhirPublication; @@ -109,7 +112,7 @@ public class ComparisonRenderer implements IEvaluationContext { if (comp.fhirType().equals(name)) { if (first) { first = false; - b.append(""+Utilities.pluralize(name, 2)+"\r\n"); + b.append(""+Utilities.pluralize(name, 2)+"\r\n"); } try { renderComparison(id, comp); @@ -143,7 +146,7 @@ public class ComparisonRenderer implements IEvaluationContext { } } - private void renderComparison(String id, ResourceComparison comp) throws IOException { + private void renderComparison(String id, ResourceComparison comp) throws IOException, FHIRFormatError, DefinitionException, FHIRException, EOperationOutcome { if (comp instanceof ProfileComparison) { renderProfile(id, (ProfileComparison) comp); } else if (comp instanceof ValueSetComparison) { @@ -169,7 +172,7 @@ public class ComparisonRenderer implements IEvaluationContext { TextFile.stringToFile(cnt, file(comp.getId()+".html")); } - private void renderCodeSystem(String id, CodeSystemComparison comp) throws IOException { + private void renderCodeSystem(String id, CodeSystemComparison comp) throws IOException, FHIRFormatError, DefinitionException, FHIRException, EOperationOutcome { String template = templates.get("CodeSystem"); Map vars = new HashMap<>(); CodeSystemComparer cs = new CodeSystemComparer(session); @@ -183,17 +186,32 @@ public class ComparisonRenderer implements IEvaluationContext { vars.put("errors", new StringType(new XhtmlComposer(true).compose(cs.renderErrors(comp)))); vars.put("metadata", new StringType(new XhtmlComposer(true).compose(cs.renderMetadata(comp, "", "")))); vars.put("concepts", new StringType(new XhtmlComposer(true).compose(cs.renderConcepts(comp, "", "")))); + String cnt = processTemplate(template, "CodeSystem", vars); TextFile.stringToFile(cnt, file(comp.getId()+".html")); new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path(folder, comp.getId() + "-union.json")), comp.getUnion()); new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path(folder, comp.getId() + "-intersection.json")), comp.getIntersection()); + + String union = new XhtmlComposer(true).compose(cs.renderUnion(comp, "", folder, "http://hl7.org/fhir")); + String intersection = new XhtmlComposer(true).compose(cs.renderIntersection(comp, "", folder, "http://hl7.org/fhir")); + vars.put("union", new StringType(union)); + vars.put("intersection", new StringType(intersection)); + + template = templates.get("CodeSystem-Union"); + cnt = processTemplate(template, "CodeSystem-Union", vars); + TextFile.stringToFile(cnt, file(comp.getId()+"-union.html")); + + template = templates.get("CodeSystem-Intersection"); + cnt = processTemplate(template, "CodeSystem-Intersection", vars); + TextFile.stringToFile(cnt, file(comp.getId()+"-intersection.html")); + } private String file(String name) throws IOException { return Utilities.path(folder, name); } - private void renderValueSet(String id, ValueSetComparison comp) throws FHIRException, IOException { + private void renderValueSet(String id, ValueSetComparison comp) throws FHIRException, IOException, EOperationOutcome { String template = templates.get("ValueSet"); Map vars = new HashMap<>(); ValueSetComparer cs = new ValueSetComparer(session); @@ -212,6 +230,19 @@ public class ComparisonRenderer implements IEvaluationContext { TextFile.stringToFile(cnt, file(comp.getId()+".html")); new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path(folder, comp.getId() + "-union.json")), comp.getUnion()); new org.hl7.fhir.r5.formats.JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(Utilities.path(folder, comp.getId() + "-intersection.json")), comp.getIntersection()); + + String union = new XhtmlComposer(true).compose(cs.renderUnion(comp, "", folder, "http://hl7.org/fhir")); + String intersection = new XhtmlComposer(true).compose(cs.renderIntersection(comp, "", folder, "http://hl7.org/fhir")); + vars.put("union", new StringType(union)); + vars.put("intersection", new StringType(intersection)); + + template = templates.get("ValueSet-Union"); + cnt = processTemplate(template, "ValueSet-Union", vars); + TextFile.stringToFile(cnt, file(comp.getId()+"-union.html")); + + template = templates.get("ValueSet-Intersection"); + cnt = processTemplate(template, "ValueSet-Intersection", vars); + TextFile.stringToFile(cnt, file(comp.getId()+"-intersection.html")); } private void renderProfile(String id, ProfileComparison comp) throws IOException { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java index 2d37d4fab..2a323a142 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ResourceComparer.java @@ -202,7 +202,7 @@ public class ResourceComparer { s = s + "Failed"; color = COLOR_ISSUE; } - s = s + ""+(e != null ? Utilities.escapeXml(e.getMessage()) : "")+""; + s = s + ""+(e != null ? Utilities.escapeXml(e.getMessage()) : "")+""; return ""+s+"\r\n"; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java index 3594ac32f..c9a98df2c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ValueSetComparer.java @@ -7,6 +7,8 @@ import java.util.List; import org.hl7.fhir.exceptions.DefinitionException; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.comparison.CodeSystemComparer.CodeSystemComparison; import org.hl7.fhir.r5.context.IWorkerContext; import org.hl7.fhir.r5.model.CanonicalType; import org.hl7.fhir.r5.model.Element; @@ -16,10 +18,18 @@ import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; import org.hl7.fhir.r5.model.ValueSet.ConceptSetFilterComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetComposeComponent; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; +import org.hl7.fhir.r5.renderers.CodeSystemRenderer; +import org.hl7.fhir.r5.renderers.ValueSetRenderer; +import org.hl7.fhir.r5.renderers.utils.RenderingContext; +import org.hl7.fhir.r5.renderers.utils.ResourceWrapper; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.GenerationRules; +import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; +import org.hl7.fhir.r5.utils.EOperationOutcome; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.RenderingI18nContext; import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationMessage.Source; @@ -904,4 +914,15 @@ public class ValueSetComparer extends CanonicalResourceComparer { return false; } + + public XhtmlNode renderUnion(ValueSetComparison comp, String id, String prefix, String corePath) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome { + ValueSetRenderer vsr = new ValueSetRenderer(new RenderingContext(session.getContextLeft(), null, new ValidationOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER)); + return vsr.buildNarrative(ResourceWrapper.forResource(vsr.getContext(), comp.union)); + } + + public XhtmlNode renderIntersection(ValueSetComparison comp, String id, String prefix, String corePath) throws FHIRFormatError, DefinitionException, FHIRException, IOException, EOperationOutcome { + ValueSetRenderer vsr = new ValueSetRenderer(new RenderingContext(session.getContextLeft(), null, new ValidationOptions(), corePath, prefix, null, ResourceRendererMode.TECHNICAL, GenerationRules.IG_PUBLISHER)); + return vsr.buildNarrative(ResourceWrapper.forResource(vsr.getContext(), comp.intersection)); + } + } \ No newline at end of file From caf50032157589796fffc47fa345cf2d41724636 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Dec 2024 06:48:02 +0300 Subject: [PATCH 09/19] Do not use metadata from data type profiles on elements when generating snapshots --- .../hl7/fhir/r5/conformance/profile/ProfileUtilities.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java index 406ee20a2..89465ec1a 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/profile/ProfileUtilities.java @@ -2450,7 +2450,10 @@ public class ProfileUtilities { } } } + // Before applying changes, apply them to what's in the profile + // but only if it's an extension or a resource + StructureDefinition profile = null; boolean msg = true; if (base.hasSliceName()) { @@ -2482,7 +2485,7 @@ public class ProfileUtilities { msg = false; } } - if (profile != null) { + if (profile != null && (profile.getKind() == StructureDefinitionKind.RESOURCE || "Extension".equals(profile.getType()))) { if (profile.getSnapshot().getElement().isEmpty()) { throw new DefinitionException(context.formatMessage(I18nConstants.SNAPSHOT_IS_EMPTY, profile.getVersionedUrl())); } From cff693eb10eed4ce87399bb06f8f95415ae63cde Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Dec 2024 06:49:00 +0300 Subject: [PATCH 10/19] Add supplements for used systems as well as for value set systems when validating on server --- .../fhir/r5/context/BaseWorkerContext.java | 30 +++++++++++++++---- 1 file changed, 25 insertions(+), 5 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java index 242be319e..f3513fef7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/BaseWorkerContext.java @@ -1375,7 +1375,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } try { Parameters pIn = constructParameters(options, code); - res = validateOnServer(tc, vs, pIn, options); + res = validateOnServer2(tc, vs, pIn, options, systems); } catch (Exception e) { res = new ValidationResult(IssueSeverity.ERROR, e.getMessage() == null ? e.getClass().getName() : e.getMessage(), null).setTxLink(txLog == null ? null : txLog.getLastId()).setErrorClass(TerminologyServiceErrorClass.SERVER_ERROR); } @@ -1645,7 +1645,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte txLog("$validate "+txCache.summary(code)+" for "+ txCache.summary(vs)+" on "+tc.getAddress()); try { Parameters pIn = constructParameters(options, code); - res = validateOnServer(tc, vs, pIn, options); + res = validateOnServer2(tc, vs, pIn, options, systems); } catch (Exception e) { issues.clear(); OperationOutcomeIssueComponent iss = new OperationOutcomeIssueComponent(org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR, org.hl7.fhir.r5.model.OperationOutcome.IssueType.EXCEPTION); @@ -1727,6 +1727,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } protected ValidationResult validateOnServer(TerminologyClientContext tc, ValueSet vs, Parameters pin, ValidationOptions options) throws FHIRException { + return validateOnServer2(tc, vs, pin, options, null); + } + + protected ValidationResult validateOnServer2(TerminologyClientContext tc, ValueSet vs, Parameters pin, ValidationOptions options, Set systems) throws FHIRException { if (vs != null) { for (ConceptSetComponent inc : vs.getCompose().getInclude()) { @@ -1737,7 +1741,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } } - addServerValidationParameters(tc, vs, pin, options); + addServerValidationParameters(tc, vs, pin, options, systems); if (txLog != null) { txLog.clearLastId(); @@ -1755,6 +1759,10 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } protected void addServerValidationParameters(TerminologyClientContext terminologyClientContext, ValueSet vs, Parameters pin, ValidationOptions options) { + addServerValidationParameters(terminologyClientContext, vs, pin, options, null); + } + + protected void addServerValidationParameters(TerminologyClientContext terminologyClientContext, ValueSet vs, Parameters pin, ValidationOptions options, Set systems) { boolean cache = false; if (vs != null) { if (terminologyClientContext != null && terminologyClientContext.isTxCaching() && terminologyClientContext.getCacheId() != null && vs.getUrl() != null && terminologyClientContext.getCached().contains(vs.getUrl()+"|"+ vs.getVersion())) { @@ -1777,6 +1785,11 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte cache = true; addDependentResources(terminologyClientContext, pin, vs); } + if (systems != null) { + for (String s : systems) { + cache = addDependentCodeSystem(terminologyClientContext, pin, s, null) || cache; + } + } pin.addParameter().setName("cache-id").setValue(new IdType(terminologyClientManager.getCacheId())); for (ParametersParameterComponent pp : pin.getParameter()) { if (pp.getName().equals("profile")) { @@ -1823,13 +1836,20 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } } } - CodeSystem cs = fetchResource(CodeSystem.class, inc.getSystem(), src); + String sys = inc.getSystem(); + cache = addDependentCodeSystem(tc, pin, sys, src) || cache; + return cache; + } + + public boolean addDependentCodeSystem(TerminologyClientContext tc, Parameters pin, String sys, Resource src) { + boolean cache = false; + CodeSystem cs = fetchResource(CodeSystem.class, sys, src); if (cs != null && !hasCanonicalResource(pin, "tx-resource", cs.getVUrl()) && (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == CodeSystemContentMode.FRAGMENT)) { cache = checkAddToParams(tc, pin, cs) || cache; } for (CodeSystem supp : codeSystems.getSupplements(cs)) { //if (supp.getContent() == CodeSystemContentMode.SUPPLEMENT && supp.getSupplements().equals(inc.getSystem())) { - assert supp.getContent() == CodeSystemContentMode.SUPPLEMENT && supp.getSupplements().equals(inc.getSystem()); + assert supp.getContent() == CodeSystemContentMode.SUPPLEMENT && supp.getSupplements().equals(sys); cache = checkAddToParams(tc, pin, supp) || cache; //} } From 7d54780cadab76da71cb04bc9c51325772aee4d4 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Dec 2024 06:49:22 +0300 Subject: [PATCH 11/19] hide API-Key from appearing on the tx log --- .../java/org/hl7/fhir/r5/context/TextClientLogger.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TextClientLogger.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TextClientLogger.java index 836f528b9..6f2e1fda7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TextClientLogger.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/context/TextClientLogger.java @@ -63,8 +63,13 @@ public class TextClientLogger extends BaseLogger implements ToolingClientLogger file.println("\r\n--- "+id+" -----------------\r\nRequest: \r\n"); file.println(method+" "+url+" HTTP/1.0"); if (headers != null) { - for (String s : headers) - file.println(s); + for (String s : headers) { + if (s.startsWith("Api-Key")) { + file.println("Api-Key: xxxxxxxxxxxxxxxx"); + } else { + file.println(s); + } + } } if (body != null) { file.println(""); From 1cdb7ea6956307aaa45969647c261674fe81db76 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Dec 2024 06:50:31 +0300 Subject: [PATCH 12/19] questionnaire rendering improvements --- .../r5/renderers/DiagnosticReportRenderer.java | 2 +- .../fhir/r5/renderers/QuestionnaireRenderer.java | 4 ++-- .../hl7/fhir/r5/renderers/RendererFactory.java | 1 + .../hl7/fhir/r5/renderers/ResourceRenderer.java | 16 +++++++++++++--- .../r5/renderers/utils/ResourceWrapperModel.java | 2 +- 5 files changed, 18 insertions(+), 7 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java index 3a8faa241..c5a26bda6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java @@ -99,7 +99,7 @@ public class DiagnosticReportRenderer extends ResourceRenderer { for (ResourceWrapper cont : dr.children("contained")) { x.hr(); - RendererFactory.factory(cont, context.forContained()).buildNarrative(status, x, cont); + RendererFactory.factory(cont, context.forContained()).setInner(true).buildNarrative(status, x, cont); } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java index 20d3166f0..b635a37a4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireRenderer.java @@ -75,7 +75,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { first = false; } x.hr(); - RendererFactory.factory(cont, context.forContained()).buildNarrative(status, x, cont); + RendererFactory.factory(cont, context.forContained()).setInner(true).buildNarrative(status, x, cont); } } @@ -565,7 +565,7 @@ public class QuestionnaireRenderer extends TerminologyRenderer { if (i.has("answerValueSet")) { if (!defn.getPieces().isEmpty()) defn.addPiece(gen.new Piece("br")); defn.getPieces().add(gen.new Piece(null, (context.formatPhrase(RenderingContext.QUEST_VALUE)+" "), null)); - if (Utilities.noString(i.primitiveValue("answerValueSet")) && i.primitiveValue("answerValueSet").startsWith("#")) { + if (Utilities.noString(i.primitiveValue("answerValueSet")) && i.primitiveValue("answerValueSet").startsWith("#")) { ResourceWrapper vs = q.getContained(i.primitiveValue("answerValueSet").substring(1)); if (vs == null) { defn.getPieces().add(gen.new Piece(null, i.primitiveValue("answerValueSet"), null)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java index a557f287a..92149ae8f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/RendererFactory.java @@ -76,6 +76,7 @@ public class RendererFactory { case "Patient": return new PatientRenderer(context); case "Provenance": return new ProvenanceRenderer(context); case "Parameters": return new ParametersRenderer(context); + case "Questionnaire": return new QuestionnaireRenderer(context); case "QuestionnaireResponse": return new QuestionnaireResponseRenderer(context); } if (resource.isDirect()) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java index ba4d22bf4..f38131309 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java @@ -51,6 +51,7 @@ public abstract class ResourceRenderer extends DataRenderer { protected XVerExtensionManager xverManager; protected boolean multiLangMode; + protected boolean inner; public ResourceRenderer(RenderingContext context) { @@ -70,6 +71,15 @@ public abstract class ResourceRenderer extends DataRenderer { return false; } + public boolean isInner() { + return inner; + } + + public ResourceRenderer setInner(boolean inner) { + this.inner = inner; + return this; + } + /** * Just build the narrative that would go in the resource (per @renderResource()), but don't put it in the resource * @param dr @@ -837,9 +847,9 @@ public abstract class ResourceRenderer extends DataRenderer { XhtmlNode p = x.para().attribute("class", "res-header-id"); String ft = context.getTranslatedCode(r.fhirType(), "http://hl7.org/fhir/fhir-types"); if (desc == null) { - p.b().tx(context.formatPhrase(context.isTechnicalMode() ? RenderingContext.PROF_DRIV_GEN_NARR_TECH : RenderingContext.PROF_DRIV_GEN_NARR, ft, "")); + p.b().tx(context.formatPhrase(context.isTechnicalMode() && !isInner() ? RenderingContext.PROF_DRIV_GEN_NARR_TECH : RenderingContext.PROF_DRIV_GEN_NARR, ft, "")); } else { - p.b().tx(context.formatPhrase(context.isTechnicalMode() ? RenderingContext.PROF_DRIV_GEN_NARR_TECH : RenderingContext.PROF_DRIV_GEN_NARR, ft, desc)); + p.b().tx(context.formatPhrase(context.isTechnicalMode() && !isInner() ? RenderingContext.PROF_DRIV_GEN_NARR_TECH : RenderingContext.PROF_DRIV_GEN_NARR, ft, desc)); } // first thing we do is lay down the resource anchors. @@ -1464,7 +1474,7 @@ public abstract class ResourceRenderer extends DataRenderer { context.addAnchor(id); x.an(context.prefixAnchor(id)); } - RendererFactory.factory(c, context.forContained()).buildNarrative(status, x, c); + RendererFactory.factory(c, context.forContained()).setInner(true).buildNarrative(status, x, c); } } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ResourceWrapperModel.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ResourceWrapperModel.java index b64c98f24..b97dd2e1c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ResourceWrapperModel.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/utils/ResourceWrapperModel.java @@ -256,7 +256,7 @@ public class ResourceWrapperModel extends ResourceWrapper { } public String getWebPath() { - return null; + return model.getWebPath(); } public String getCodeSystemUri() { From 0ba7d8434c67fa607526d2e444f4c8c29bd2f121 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Dec 2024 06:51:16 +0300 Subject: [PATCH 13/19] Add support for using Liquid on plain JSON directly and add support for markdownify filter --- .../client/TerminologyClientManager.java | 2 +- .../hl7/fhir/r5/utils/BaseJsonWrapper.java | 87 +++++++++++++++++++ .../org/hl7/fhir/r5/utils/LiquidEngine.java | 35 +++++++- 3 files changed, 121 insertions(+), 3 deletions(-) create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/BaseJsonWrapper.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java index 5add4914a..00897c519 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java @@ -331,7 +331,7 @@ public class TerminologyClientManager { private String host() throws MalformedURLException { URL url = new URL(getMasterClient().getAddress()); - if (url.getPort() != 0) { + if (url.getPort() > 0) { return url.getHost()+":"+url.getPort(); } else { return url.getHost(); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/BaseJsonWrapper.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/BaseJsonWrapper.java new file mode 100644 index 000000000..f4a719af3 --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/BaseJsonWrapper.java @@ -0,0 +1,87 @@ +package org.hl7.fhir.r5.utils; + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.utilities.FhirPublication; +import org.hl7.fhir.utilities.json.model.JsonArray; +import org.hl7.fhir.utilities.json.model.JsonElement; + +// this class exists to allow the Liquid Engine to be used against raw JSON + +public class BaseJsonWrapper extends Base { + + private static final long serialVersionUID = 1L; + private JsonElement j; + + public BaseJsonWrapper(JsonElement j) { + super(); + this.j = j; + } + + @Override + public String fhirType() { + switch (j.type()) { + case BOOLEAN: return "boolean"; + case NUMBER: return "decimal"; + case OBJECT: return "Object"; + case STRING: return "string"; + default: + throw new Error("Shouldn't get here"); + } + } + + @Override + public String getIdBase() { + if (j.isJsonObject()) { + return j.asJsonObject().asString("id"); + } else { + return null; + } + } + + @Override + public void setIdBase(String value) { + throw new Error("BaseJsonWrapper is read only"); + } + + @Override + public Base copy() { + throw new Error("BaseJsonWrapper is read only"); + } + + @Override + public FhirPublication getFHIRPublicationVersion() { + return FhirPublication.R5; + } + + public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { + if (j.isJsonObject() && j.asJsonObject().has(name)) { + JsonElement e = j.asJsonObject().get(name); + if (e.isJsonArray()) { + JsonArray a = e.asJsonArray(); + Base[] l = new Base[a.size()]; + for (int i = 0; i < a.size(); i++) { + l[i] = new BaseJsonWrapper(a.get(i)); + } + return l; + } else { + Base[] l = new Base[1]; + l[0] = new BaseJsonWrapper(e); + return l; + } + } else { + return super.getProperty(hash, name, checkValid); + } + } + + @Override + public String toString() { + if (j.isJsonPrimitive()) { + return j.asString(); + } else { + return super.toString(); + } + } + + +} diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java index fb7574ec9..48229cb96 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/LiquidEngine.java @@ -49,7 +49,9 @@ import org.hl7.fhir.r5.fhirpath.FHIRPathUtilityClasses.FunctionDetails; import org.hl7.fhir.r5.model.Base; import org.hl7.fhir.r5.model.Tuple; import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.utilities.MarkDownProcessor; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.MarkDownProcessor.Dialect; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.xhtml.NodeType; import org.hl7.fhir.utilities.xhtml.XhtmlNode; @@ -68,7 +70,8 @@ public class LiquidEngine implements IEvaluationContext { private FHIRPathEngine engine; private ILiquidEngineIncludeResolver includeResolver; private ILiquidRenderingSupport renderingSupport; - + private MarkDownProcessor processor = new MarkDownProcessor(Dialect.COMMON_MARK); + private class LiquidEngineContext { private Object externalContext; private Map loopVars = new HashMap<>(); @@ -162,12 +165,24 @@ public class LiquidEngine implements IEvaluationContext { } private enum LiquidFilter { - PREPEND; + PREPEND, + MARKDOWNIFY, + UPCASE, + DOWNCASE; public static LiquidFilter fromCode(String code) { if ("prepend".equals(code)) { return PREPEND; } + if ("markdownify".equals(code)) { + return MARKDOWNIFY; + } + if ("upcase".equals(code)) { + return UPCASE; + } + if ("downcase".equals(code)) { + return DOWNCASE; + } return null; } } @@ -221,12 +236,25 @@ public class LiquidEngine implements IEvaluationContext { } else switch (i.filter) { case PREPEND: t = stmtToString(ctxt, engine.evaluate(ctxt, resource, resource, resource, i.expression)) + t; + break; + case MARKDOWNIFY: + t = processMarkdown(t); + break; + case UPCASE: + t = t.toUpperCase(); + break; + case DOWNCASE: + t = t.toLowerCase(); break; } } b.append(t); } + private String processMarkdown(String t) { + return processor.process(t, "liquid"); + } + private String stmtToString(LiquidEngineContext ctxt, List items) { StringBuilder b = new StringBuilder(); boolean first = true; @@ -489,6 +517,9 @@ public class LiquidEngine implements IEvaluationContext { public LiquidParser(String source) { this.source = source; + if (source == null) { + throw new FHIRException("No Liquid source to parse"); + } cursor = 0; } From 3538428d986b613c57f05db558b6323d044c51d0 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Dec 2024 06:51:31 +0300 Subject: [PATCH 14/19] new release of pubpack --- .../fhir/r5/test/SnapShotGenerationTests.java | 23 +++- .../org/hl7/fhir/r5/utils/LiquidJsonTest.java | 130 ++++++++++++++++++ 2 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/LiquidJsonTest.java diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java index 0ad0f7615..fa40aa10d 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/test/SnapShotGenerationTests.java @@ -115,6 +115,7 @@ public class SnapShotGenerationTests { private StructureDefinition expected; private StructureDefinition output; public AllowUnknownProfile allow; + private boolean json; public TestDetails(Element test) { super(); @@ -202,8 +203,14 @@ public class SnapShotGenerationTests { source = (StructureDefinition) new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", "snapshot-generation", id + "-input.json")); else source = (StructureDefinition) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "snapshot-generation", id + "-input.xml")); - if (!fail) - expected = (StructureDefinition) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "snapshot-generation", id + "-expected.xml")); + if (!fail) { + if (TestingUtilities.findTestResource("r5", "snapshot-generation", id + "-expected.json")) { + json = true; + expected = (StructureDefinition) new JsonParser().parse(TestingUtilities.loadTestResourceStream("r5", "snapshot-generation", id + "-expected.json")); + } else { + expected = (StructureDefinition) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "snapshot-generation", id + "-expected.xml")); + } + } if (!Utilities.noString(include)) included.add((StructureDefinition) new XmlParser().parse(TestingUtilities.loadTestResourceStream("r5", "snapshot-generation", include + ".xml"))); if (!Utilities.noString(register)) { @@ -589,12 +596,16 @@ public class SnapShotGenerationTests { if (!fail) { test.output = output; TestingUtilities.getSharedWorkerContext().cacheResource(output); - File dst = ManagedFileAccess.file(TestingUtilities.tempFile("snapshot", test.getId() + "-expected.xml")); - if (dst.exists()) + File dst = ManagedFileAccess.file(TestingUtilities.tempFile("snapshot", test.getId() + "-expected" + (test.json ? ".json" : ".xml"))); + if (dst.exists()) { dst.delete(); + } // IOUtils.copy(TestingUtilities.loadTestResourceStream("r5", "snapshot-generation", test.getId() + "-expected.xml"), ManagedFileAccess.outStream(dst)); - String actualFilePath = TestingUtilities.tempFile("snapshot", test.getId() + "-expected.xml"); - new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(actualFilePath), output); + if (test.json) { + new JsonParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(dst.getAbsolutePath()), output); + } else { + new XmlParser().setOutputStyle(OutputStyle.PRETTY).compose(ManagedFileAccess.outStream(dst.getAbsolutePath()), output); + } StructureDefinition t1 = test.expected.copy(); t1.setText(null); StructureDefinition t2 = test.output.copy(); diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/LiquidJsonTest.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/LiquidJsonTest.java new file mode 100644 index 000000000..3607c06d1 --- /dev/null +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/utils/LiquidJsonTest.java @@ -0,0 +1,130 @@ +package org.hl7.fhir.r5.utils; + +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.PathEngineException; +import org.hl7.fhir.r5.conformance.profile.ProfileUtilities; +import org.hl7.fhir.r5.context.ContextUtilities; +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.fhirpath.FHIRPathEngine; +import org.hl7.fhir.r5.fhirpath.FHIRPathEngine.IEvaluationContext; +import org.hl7.fhir.r5.fhirpath.FHIRPathUtilityClasses.FunctionDetails; +import org.hl7.fhir.r5.fhirpath.TypeDetails; +import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.r5.test.utils.TestingUtilities; +import org.hl7.fhir.r5.utils.LiquidEngine.LiquidDocument; +import org.hl7.fhir.utilities.json.JsonException; +import org.hl7.fhir.utilities.json.model.JsonObject; +import org.hl7.fhir.utilities.json.parser.JsonParser; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.Test; + +public class LiquidJsonTest implements IEvaluationContext { + + private IWorkerContext ctxt; + private ContextUtilities cu; + private ProfileUtilities pu; + + @Test + void testHistory() throws JsonException, IOException { + init(); + + JsonObject json = JsonParser.parseObject(TestingUtilities.loadTestResource("r5", "liquid-json", "history.json")); + LiquidEngine liquid = new LiquidEngine(ctxt, this); + LiquidDocument template = liquid.parse(TestingUtilities.loadTestResource("r5", "liquid-json", "history.liquid"), null); + BaseJsonWrapper base = new BaseJsonWrapper(json); + String s = liquid.evaluate(template, base, this).trim(); + String expected = TestingUtilities.loadTestResource("r5", "liquid-json", "history.html").trim(); + System.out.println(s); + Assertions.assertEquals(expected, s); + } + + @Test + void testTestCases() throws JsonException, IOException { + init(); + + JsonObject json = JsonParser.parseObject(TestingUtilities.loadTestResource("r5", "liquid-json", "test-cases.json")); + LiquidEngine liquid = new LiquidEngine(ctxt, this); + LiquidDocument template = liquid.parse(TestingUtilities.loadTestResource("r5", "liquid-json", "test-cases.liquid"), null); + BaseJsonWrapper base = new BaseJsonWrapper(json); + String s = liquid.evaluate(template, base, this).trim(); + String expected = TestingUtilities.loadTestResource("r5", "liquid-json", "test-cases.html").trim(); + System.out.println(s); + Assertions.assertEquals(expected, s); + } + + public void init() { + if (pu == null) { + ctxt = TestingUtilities.getSharedWorkerContext(); + cu = new ContextUtilities(ctxt); + pu = new ProfileUtilities(ctxt, null, cu); + } + } + + @Override + public List resolveConstant(FHIRPathEngine engine, Object appContext, String name, boolean beforeContext, boolean explicitConstant) throws PathEngineException { + return new ArrayList(); + } + + @Override + public TypeDetails resolveConstantType(FHIRPathEngine engine, Object appContext, String name, + boolean explicitConstant) throws PathEngineException { + return null; + } + + @Override + public boolean log(String argument, List focus) { + // TODO Auto-generated method stub + return false; + } + + @Override + public FunctionDetails resolveFunction(FHIRPathEngine engine, String functionName) { + // TODO Auto-generated method stub + return null; + } + + @Override + public TypeDetails checkFunction(FHIRPathEngine engine, Object appContext, String functionName, TypeDetails focus, + List parameters) throws PathEngineException { + // TODO Auto-generated method stub + return null; + } + + @Override + public List executeFunction(FHIRPathEngine engine, Object appContext, List focus, String functionName, + List> parameters) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Base resolveReference(FHIRPathEngine engine, Object appContext, String url, Base refContext) + throws FHIRException { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean conformsToProfile(FHIRPathEngine engine, Object appContext, Base item, String url) + throws FHIRException { + // TODO Auto-generated method stub + return false; + } + + @Override + public ValueSet resolveValueSet(FHIRPathEngine engine, Object appContext, String url) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean paramIsType(String name, int index) { + // TODO Auto-generated method stub + return false; + } +} From e59e752567e5668d0d8873548ab247967c18487c Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Dec 2024 06:52:18 +0300 Subject: [PATCH 15/19] start working on using new IG infrastructure for Tx tests --- .../org/hl7/fhir/utilities/Utilities.java | 8 ++- .../fhir/utilities/npm/CommonPackages.java | 2 +- .../xhtml/HierarchicalTableGenerator.java | 3 +- .../hl7/fhir/validation/special/TxTester.java | 17 ++++- .../tests/SnapShotGenerationXTests.java | 72 +++++++++++-------- .../ExternalTerminologyServiceTests.java | 5 ++ .../tests/LocalTerminologyServiceTests.java | 7 ++ .../terminology/tests/OntoserverTests.java | 5 ++ ...v_Fachabteilungsschluessel-erweitert.cache | 24 +++++++ 9 files changed, 107 insertions(+), 36 deletions(-) create mode 100644 org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/http___fhir.de_CodeSystem_dkgev_Fachabteilungsschluessel-erweitert.cache diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java index 8694b9417..a5e64be4f 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/Utilities.java @@ -628,9 +628,11 @@ public class Utilities { public static String padRight(String src, char c, int len) { StringBuilder s = new StringBuilder(); - s.append(src); - for (int i = 0; i < len - src.length(); i++) - s.append(c); + if (src != null) { + s.append(src); + for (int i = 0; i < len - src.length(); i++) + s.append(c); + } return s.toString(); } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java index 73e97fd1f..4730d2414 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/npm/CommonPackages.java @@ -6,6 +6,6 @@ public class CommonPackages { public static final String VER_XVER = "0.1.0"; public static final String ID_PUBPACK = "hl7.fhir.pubpack"; - public static final String VER_PUBPACK = "0.1.9"; + public static final String VER_PUBPACK = "0.2.0"; } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java index b4cb4bc5d..d743f1c7a 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/HierarchicalTableGenerator.java @@ -178,8 +178,9 @@ public class HierarchicalTableGenerator { return style; } - public void setTag(String tag) { + public Piece setTag(String tag) { this.tag = tag; + return this; } public Piece setText(String text) { diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java index 14f96efdd..fd926c621 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java @@ -56,6 +56,7 @@ public class TxTester { public String describe(); public Resource loadResource(String filename) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException; public byte[] loadContent(String filename) throws FileNotFoundException, IOException; + public boolean hasContent(String filename) throws IOException; } private String server; @@ -172,7 +173,16 @@ public class TxTester { } private String loadVersion() throws JsonException, IOException { - return processHistoryMarkdown(loader.loadContent("history.md")); + if (loader.hasContent("history.json")) { + return readHistory(loader.loadContent("history.json")); + } else { + return processHistoryMarkdown(loader.loadContent("history.md")); + } + } + + private String readHistory(byte[] content) throws JsonException, IOException { + JsonObject json = JsonParser.parseObject(content); + return json.getJsonObjects("versions").get(0).asString("version"); } public static String processHistoryMarkdown(byte[] content) throws IOException { @@ -650,6 +660,11 @@ public class TxTester { public byte[] loadContent(String filename) throws FileNotFoundException, IOException { return TextFile.fileToBytes(Utilities.path(folder, filename)); } + + @Override + public boolean hasContent(String filename) throws IOException { + return new File(Utilities.path(folder, filename)).exists(); + } } } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java index 9f4d5cc56..8582f9752 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/SnapShotGenerationXTests.java @@ -101,7 +101,7 @@ public class SnapShotGenerationXTests { private List rules = new ArrayList<>(); private StructureDefinition source; - private StructureDefinition included; + private List included = new ArrayList(); private StructureDefinition expected; private StructureDefinition output; public boolean outputIsJson; @@ -146,7 +146,7 @@ public class SnapShotGenerationXTests { return fail; } - public StructureDefinition getIncluded() { + public List getIncluded() { return included; } @@ -191,12 +191,14 @@ public class SnapShotGenerationXTests { expected = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", id + "-output.xml")); } if (!Utilities.noString(include)) - included = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", include + ".xml")); + included.add((StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", include + ".xml"))); if (!Utilities.noString(register)) { - if (TestingUtilities.findTestResource("rX", "snapshot-generation", register + ".xml")) { - included = (StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", register + ".xml")); - } else { - included = (StructureDefinition) XVersionLoader.loadJson(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", register + ".json")); + for (String r : register.split("\\,")) { + if (TestingUtilities.findTestResource("rX", "snapshot-generation", r + ".xml")) { + included.add((StructureDefinition) XVersionLoader.loadXml(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", r + ".xml"))); + } else { + included.add((StructureDefinition) XVersionLoader.loadJson(version, TestingUtilities.loadTestResourceStream("rX", "snapshot-generation", r + ".json"))); + } } } } @@ -321,7 +323,7 @@ public class SnapShotGenerationXTests { else return td.getOutput(); case INCLUDE: - return td.getIncluded(); + return td.getIncluded().get(0); default: throw new FHIRException("Not done yet"); } @@ -401,8 +403,11 @@ public class SnapShotGenerationXTests { for (TestDetails t : tests) { if (t.expected != null && url.equals(t.expected.getUrl())) return t.expected; - if (t.included != null && url.equals(t.included.getUrl())) - return t.included; + for (StructureDefinition sd : t.included) { + if (url.equals(sd.getUrl())) { + return sd; + } + } } return null; } @@ -499,23 +504,26 @@ public class SnapShotGenerationXTests { List messages = new ArrayList(); ProfileUtilities pu = new ProfileUtilities(UtilitiesXTests.context(version), messages, null); pu.setNewSlicingProcessing(true); - pu.setIds(test.included, false); - pu.setAllowUnknownProfile(AllowUnknownProfile.ALL_TYPES); - StructureDefinition base = UtilitiesXTests.context(version).fetchResource(StructureDefinition.class, test.included.getBaseDefinition()); - if (base != null) { - pu.generateSnapshot(base, test.included, test.included.getUrl(), "http://test.org/profile", test.included.getName()); - } - if (!UtilitiesXTests.context(version).hasResource(StructureDefinition.class, test.included.getUrl())) - UtilitiesXTests.context(version).cacheResource(test.included); - int ec = 0; - for (ValidationMessage vm : messages) { - if (vm.getLevel() == IssueSeverity.ERROR) { - System.out.println(vm.summary()); - ec++; + for (StructureDefinition sd : test.included) { + pu.setIds(sd, false); + pu.setAllowUnknownProfile(AllowUnknownProfile.ALL_TYPES); + StructureDefinition base = UtilitiesXTests.context(version).fetchResource(StructureDefinition.class, sd.getBaseDefinition()); + if (base != null) { + pu.generateSnapshot(base, sd, sd.getUrl(), "http://test.org/profile", sd.getName()); + } + if (!UtilitiesXTests.context(version).hasResource(StructureDefinition.class, sd.getUrl())) + UtilitiesXTests.context(version).cacheResource(sd); + int ec = 0; + for (ValidationMessage vm : messages) { + if (vm.getLevel() == IssueSeverity.ERROR) { + System.out.println(vm.summary()); + ec++; + } + } + if (ec > 0) { + throw new FHIRException("register gen failed: " + messages.toString()); } } - if (ec > 0) - throw new FHIRException("register gen failed: " + messages.toString()); } StructureDefinition base = getSD(test.getSource().getBaseDefinition()); if (!base.getUrl().equals(test.getSource().getBaseDefinition())) @@ -560,14 +568,19 @@ public class SnapShotGenerationXTests { if (!fail) { test.output = output; UtilitiesXTests.context(version).cacheResource(output); - File dst = ManagedFileAccess.file(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output.xml")); + File dst = ManagedFileAccess.file(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output" + (test.outputIsJson ? ".json" : ".xml"))); if (dst.exists()) dst.delete(); if (test.outputIsJson) { - XVersionLoader.saveJson(version, output, ManagedFileAccess.outStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output.json"))); + XVersionLoader.saveJson(version, output, ManagedFileAccess.outStream(dst.getAbsolutePath())); } else { - XVersionLoader.saveXml(version, output, ManagedFileAccess.outStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-output.json"))); - } + XVersionLoader.saveXml(version, output, ManagedFileAccess.outStream(dst.getAbsolutePath())); + } + if (test.outputIsJson) { + XVersionLoader.saveJson(version, test.expected, ManagedFileAccess.outStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-expected" + (test.outputIsJson ? ".json" : ".xml")))); + } else { + XVersionLoader.saveXml(version, test.expected, ManagedFileAccess.outStream(UtilitiesXTests.tempFile("snapshot", test.getId() + "-expected" + (test.outputIsJson ? ".json" : ".xml")))); + } StructureDefinition t1 = test.expected.copy(); t1.setText(null); StructureDefinition t2 = test.output.copy(); @@ -583,7 +596,6 @@ public class SnapShotGenerationXTests { return path; } - private StructureDefinition getSD(String url) throws DefinitionException, FHIRException, IOException { StructureDefinition sd = context.getByUrl(url); if (sd == null) diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java index 077b5faa7..ea5d8a483 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/ExternalTerminologyServiceTests.java @@ -147,4 +147,9 @@ public class ExternalTerminologyServiceTests implements ITxTesterLoader { public byte[] loadContent(String filename) throws FileNotFoundException, IOException { return TestingUtilities.loadTestResourceBytes("tx", filename); } + + @Override + public boolean hasContent(String filename) throws IOException { + return TestingUtilities.findTestResource("tx", filename); + } } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/LocalTerminologyServiceTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/LocalTerminologyServiceTests.java index 27bfaf9ef..99ae9f81e 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/LocalTerminologyServiceTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/LocalTerminologyServiceTests.java @@ -188,4 +188,11 @@ public class LocalTerminologyServiceTests implements ITxTesterLoader { public byte[] loadContent(String filename) throws FileNotFoundException, IOException { return TestingUtilities.loadTestResourceBytes("tx", filename); } + + + + @Override + public boolean hasContent(String filename) throws IOException { + return TestingUtilities.findTestResource("tx", filename); + } } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/OntoserverTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/OntoserverTests.java index d20136497..1acd93678 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/OntoserverTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/OntoserverTests.java @@ -174,4 +174,9 @@ public class OntoserverTests implements ITxTesterLoader { public byte[] loadContent(String filename) throws FileNotFoundException, IOException { return TestingUtilities.loadTestResourceBytes("tx", filename); } + + @Override + public boolean hasContent(String filename) throws IOException { + return TestingUtilities.findTestResource("tx", filename); + } } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/http___fhir.de_CodeSystem_dkgev_Fachabteilungsschluessel-erweitert.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/http___fhir.de_CodeSystem_dkgev_Fachabteilungsschluessel-erweitert.cache new file mode 100644 index 000000000..262120014 --- /dev/null +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/http___fhir.de_CodeSystem_dkgev_Fachabteilungsschluessel-erweitert.cache @@ -0,0 +1,24 @@ +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://fhir.de/CodeSystem/dkgev/Fachabteilungsschluessel-erweitert", + "code" : "3600" +}, "valueSet" :null, "langs":"en-US", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": { + "resourceType" : "Parameters", + "parameter" : [{ + "name" : "profile-url", + "valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891" + }] +}}#### +v: { + "display" : "Intensivmedizin", + "code" : "3600", + "system" : "http://fhir.de/CodeSystem/dkgev/Fachabteilungsschluessel-erweitert", + "version" : "1.5.0", + "server" : "http://tx-dev.fhir.org/r5", + "unknown-systems" : "", + "issues" : { + "resourceType" : "OperationOutcome" +} + +} +------------------------------------------------------------------------------------- From 833051e995fd1eca6786106354e96f326ca1ecb6 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Dec 2024 09:07:08 +0300 Subject: [PATCH 16/19] Jens code generation changes --- .../r4/profilemodel/gen/PECodeGenerator.java | 13 +++-- .../r5/profilemodel/gen/PECodeGenerator.java | 48 ++++--------------- 2 files changed, 17 insertions(+), 44 deletions(-) diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java index f2c61b840..acf197678 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/profilemodel/gen/PECodeGenerator.java @@ -30,15 +30,14 @@ package org.hl7.fhir.r4.profilemodel.gen; import java.io.IOException; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.TimeZone; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; -import com.google.common.base.Strings; import org.hl7.fhir.r4.context.IWorkerContext; import org.hl7.fhir.r4.model.ElementDefinition; import org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent; @@ -48,9 +47,9 @@ import org.hl7.fhir.r4.model.Type; import org.hl7.fhir.r4.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r4.profilemodel.PEBuilder; import org.hl7.fhir.r4.profilemodel.PEBuilder.PEElementPropertiesPolicy; -import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.r4.profilemodel.PEDefinition; import org.hl7.fhir.r4.profilemodel.PEType; +import org.hl7.fhir.r4.terminologies.ValueSetExpander.ValueSetExpansionOutcome; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; @@ -470,6 +469,10 @@ public class PECodeGenerator { w(load, " "+name+" = ("+type+") src.child(\""+fname+"\").asElement();"); } else if (Utilities.existsInList(type, workerContext.getResourceNames())) { w(load, " "+name+" = ("+type+") src.child(\""+fname+"\").asResource();"); + } else if("Reference".equals(type)) { + w(load, " "+type+" ref = ("+type+") src.child(\""+fname+"\").asDataType();"); + w(load, " if(!ref.isEmpty())"); + w(load, " "+name+" = ref;"); } else { w(load, " "+name+" = ("+type+") src.child(\""+fname+"\").asDataType();"); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java index 4c686dabf..d55bcb04b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/profilemodel/gen/PECodeGenerator.java @@ -1,62 +1,28 @@ package org.hl7.fhir.r5.profilemodel.gen; -/* - 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 \ - list of conditions and the following disclaimer. - * 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 - 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. \ - IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, \ - INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT \ - NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR \ - PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, \ - 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. - */ - -import java.io.File; import java.io.IOException; import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Date; +import java.util.HashSet; import java.util.List; import java.util.Locale; import java.util.Set; import java.util.TimeZone; -import java.util.ArrayList; -import java.util.Date; -import java.util.HashSet; import org.hl7.fhir.r5.context.IWorkerContext; -import org.hl7.fhir.r5.model.CodeableConcept; import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; -import org.hl7.fhir.r5.model.Identifier; -import org.hl7.fhir.r5.model.Observation; import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.StructureDefinition.StructureDefinitionKind; import org.hl7.fhir.r5.model.ValueSet.ValueSetExpansionContainsComponent; import org.hl7.fhir.r5.profilemodel.PEBuilder; import org.hl7.fhir.r5.profilemodel.PEBuilder.PEElementPropertiesPolicy; -import org.hl7.fhir.r5.profilemodel.gen.PECodeGenerator.ExtensionPolicy; +import org.hl7.fhir.r5.profilemodel.PEDefinition; +import org.hl7.fhir.r5.profilemodel.PEType; import org.hl7.fhir.r5.terminologies.expansion.ValueSetExpansionOutcome; import org.hl7.fhir.r5.utils.UserDataNames; -import org.hl7.fhir.r5.profilemodel.PEDefinition; -import org.hl7.fhir.r5.profilemodel.PEInstance; -import org.hl7.fhir.r5.profilemodel.PEType; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; @@ -476,6 +442,10 @@ public class PECodeGenerator { w(load, " "+name+" = ("+type+") src.child(\""+fname+"\").asElement();"); } else if (Utilities.existsInList(type, workerContext.getResourceNames())) { w(load, " "+name+" = ("+type+") src.child(\""+fname+"\").asResource();"); + } else if("Reference".equals(type)) { + w(load, " "+type+" ref = ("+type+") src.child(\""+fname+"\").asDataType();"); + w(load, " if(!ref.isEmpty())"); + w(load, " "+name+" = ref;"); } else { w(load, " "+name+" = ("+type+") src.child(\""+fname+"\").asDataType();"); } From 001af48777d724350c92fa062cf0276b0b820688 Mon Sep 17 00:00:00 2001 From: dotasek Date: Mon, 2 Dec 2024 10:56:22 -0500 Subject: [PATCH 17/19] Fix test mocks --- .../hl7/fhir/r5/context/BaseWorkerContextTests.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java index 8c6e41fa0..86369e534 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java @@ -32,13 +32,13 @@ import java.io.FileNotFoundException; import java.io.IOException; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collections; import java.util.List; import static org.junit.jupiter.api.Assertions.*; import static org.mockito.ArgumentMatchers.*; import static org.mockito.ArgumentMatchers.argThat; -import static org.mockito.Mockito.mock; -import static org.mockito.Mockito.times; +import static org.mockito.Mockito.*; @ExtendWith(MockitoExtension.class) public class BaseWorkerContextTests { @@ -374,7 +374,7 @@ public class BaseWorkerContextTests { TerminologyClientContext terminologyClientContext = context.getTxClientManager().getMaster(); - Mockito.doReturn(createdValidationResult).when(context).validateOnServer(terminologyClientContext, valueSet, pIn, validationOptions); + Mockito.doReturn(createdValidationResult).when(context).validateOnServer2(same(terminologyClientContext), same(valueSet), same(pIn), same(validationOptions), eq(Collections.emptySet())); ValidationContextCarrier ctxt = mock(ValidationContextCarrier.class); @@ -423,7 +423,7 @@ public class BaseWorkerContextTests { @Test - public void testValidateCodableConceptWithServer() throws IOException { + public void testValidateCodeableConceptWithServer() throws IOException { CodeableConcept codeableConcept = new CodeableConcept(); ValueSet valueSet = new ValueSet(); @@ -433,7 +433,7 @@ public class BaseWorkerContextTests { TerminologyClientContext terminologyClientContext = context.getTxClientManager().getMaster(); - Mockito.doReturn(createdValidationResult).when(context).validateOnServer(terminologyClientContext, valueSet, pIn, validationOptions); + Mockito.doReturn(createdValidationResult).when(context).validateOnServer2(same(terminologyClientContext), same(valueSet), same(pIn),same(validationOptions), eq(Collections.emptySet())); Mockito.doReturn(cacheToken).when(terminologyCache).generateValidationToken(validationOptions, codeableConcept, valueSet, expParameters); @@ -443,7 +443,7 @@ public class BaseWorkerContextTests { Mockito.verify(valueSetCheckerSimple, times(0)).validateCode("CodeableConcept", codeableConcept); Mockito.verify(terminologyCache).cacheValidation(eq(cacheToken), same(createdValidationResult), eq(true)); - Mockito.verify(context).validateOnServer(terminologyClientContext, valueSet, pIn, validationOptions); + Mockito.verify(context).validateOnServer2(same(terminologyClientContext), same(valueSet), same(pIn), same(validationOptions), eq(Collections.emptySet())); } @Test From bdef6f995c6336b6a992df77d3b416588004b41a Mon Sep 17 00:00:00 2001 From: dotasek Date: Mon, 2 Dec 2024 11:00:59 -0500 Subject: [PATCH 18/19] Test for new method instead of unused old one --- .../java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java index 86369e534..c4f3e0872 100644 --- a/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java +++ b/org.hl7.fhir.r5/src/test/java/org/hl7/fhir/r5/context/BaseWorkerContextTests.java @@ -418,7 +418,7 @@ public class BaseWorkerContextTests { Mockito.verify(valueSetCheckerSimple).validateCode("CodeableConcept", codeableConcept); Mockito.verify(terminologyCache).cacheValidation(eq(cacheToken), same(createdValidationResult), eq(false)); - Mockito.verify(context, times(0)).validateOnServer(any(), any(), any(), any()); + Mockito.verify(context, times(0)).validateOnServer2(any(), any(), any(), any(), any()); } From 27a94538d9afa5c972fded5d40e9fbf7ccb4a009 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 2 Dec 2024 22:49:02 +0300 Subject: [PATCH 19/19] Add support for liquid on csv files --- .../org/hl7/fhir/r5/utils/BaseCSVWrapper.java | 128 ++++++++++++++++++ 1 file changed, 128 insertions(+) create mode 100644 org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/BaseCSVWrapper.java diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/BaseCSVWrapper.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/BaseCSVWrapper.java new file mode 100644 index 000000000..4462cb64a --- /dev/null +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/BaseCSVWrapper.java @@ -0,0 +1,128 @@ +package org.hl7.fhir.r5.utils; + +import java.util.List; + +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r5.model.Base; +import org.hl7.fhir.utilities.FhirPublication; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.json.model.JsonArray; +import org.hl7.fhir.utilities.json.model.JsonElement; + +// this class exists to allow the Liquid Engine to be used against raw JSON + +public class BaseCSVWrapper extends Base { + + + private static final long serialVersionUID = 1L; + private List columns; + private List values; + private List> rows; + private String value; + + private BaseCSVWrapper() { + super(); + } + + public static BaseCSVWrapper forRows(List columns, List> rows) { + BaseCSVWrapper self = new BaseCSVWrapper(); + self.columns = columns; + self.rows = rows; + return self; + } + + public static BaseCSVWrapper forRow(List columns, List values) { + BaseCSVWrapper self = new BaseCSVWrapper(); + self.columns = columns; + self.values = values; + return self; + } + + public static BaseCSVWrapper forCell(String value) { + BaseCSVWrapper self = new BaseCSVWrapper(); + self.value = value; + return self; + } + + @Override + public String fhirType() { + if (values != null || rows != null) { + return "Object"; + } else if (Utilities.existsInList(value, "true", "false")) { + return "boolean"; + } else if (Utilities.isInteger(value)) { + return "integer"; + } else if (Utilities.isDecimal(value, true)) { + return "decimal"; + } else if (Utilities.isAbsoluteUrl(value)) { + return "url"; + } else { + return "string"; + } + } + + @Override + public String getIdBase() { + if (columns == null) { + return null; + } + + int i = columns.indexOf("id"); + if (i > -1) { + return values.get(i); + } else { + return null; + } + } + + @Override + public void setIdBase(String value) { + throw new Error("BaseCSVWrapper is read only"); + } + + @Override + public Base copy() { + throw new Error("BaseCSVWrapper is read only"); + } + + @Override + public FhirPublication getFHIRPublicationVersion() { + return FhirPublication.R5; + } + + public Base[] getProperty(int hash, String name, boolean checkValid) throws FHIRException { + if (rows != null && "rows".equals(name)) { + Base[] l = new Base[rows.size()]; + for (int i = 0; i < rows.size(); i++) { + l[i] = BaseCSVWrapper.forRow(columns, rows.get(i)); + } + return l; + } + if (values != null) { + int i = columns.indexOf(name); + if (i > -1) { + Base[] l = new Base[1]; + l[0] = BaseCSVWrapper.forCell(values.get(i)); + return l; + } + } + return super.getProperty(hash, name, checkValid); + } + + @Override + public String toString() { + return value; + } + + + @Override + public boolean isPrimitive() { + return value != null; + } + + + @Override + public String primitiveValue() { + return value; + } +}