diff --git a/.gitignore b/.gitignore index 01b1acf66..9e58ea6a0 100644 --- a/.gitignore +++ b/.gitignore @@ -304,3 +304,4 @@ local.properties /org.hl7.fhir.r5/src/test/resources/snapshot-generation/logical2-actual.xml /org.hl7.fhir.r5.new /org.hl7.fhir.r5.newc +/org.hl7.fhir.r4b.new diff --git a/README.md b/README.md index 6d21d1b7d..c5a31c519 100644 --- a/README.md +++ b/README.md @@ -162,6 +162,7 @@ compile group: 'ca.uhn.hapi.fhir', name: 'hapi-fhir-structures-r4', version: '(l compile group: 'ca.uhn.hapi.fhir', name: 'hapi-fhir-structures-r5', version: '(latest version)' ``` +Note that the built binary validator is released through GitHub releases. ### Maintenance This project is maintained by [Grahame Grieve][Link-grahameGithub], [James Agnew][Link-jamesGithub] and [Mark Iantorno][Link-markGithub] on behalf of the FHIR community. diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ExamplesPackageBuilder.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ExamplesPackageBuilder.java new file mode 100644 index 000000000..13cc57fc0 --- /dev/null +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/ExamplesPackageBuilder.java @@ -0,0 +1,74 @@ +package org.hl7.fhir.convertors.misc; + +import java.io.File; +import java.io.FileInputStream; +import java.io.FileNotFoundException; +import java.io.FileOutputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Date; +import java.util.HashSet; +import java.util.Set; + +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r4b.formats.JsonParser; +import org.hl7.fhir.r4b.model.Bundle; +import org.hl7.fhir.r4b.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.utilities.TextFile; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.json.JsonTrackingParser; + +import com.google.gson.JsonArray; +import com.google.gson.JsonObject; + +import org.hl7.fhir.r4b.model.SearchParameter; +import org.hl7.fhir.r4b.utils.NPMPackageGenerator; +import org.hl7.fhir.r4b.utils.NPMPackageGenerator.Category; + +public class ExamplesPackageBuilder { + + public static void main(String[] args) throws FHIRFormatError, FileNotFoundException, IOException { + new ExamplesPackageBuilder().process(args[0]); + + } + + private void process(String source) throws FHIRFormatError, FileNotFoundException, IOException { + Set set = new HashSet<>(); + for (File f : new File(source).listFiles()) { + if (f.getName().endsWith(".json")) { + JsonObject obj = JsonTrackingParser.parseJson(new FileInputStream(f)); + if (obj.has("resourceType") && obj.has("id")) { + String type = obj.get("resourceType").getAsString(); + String id = obj.get("id").getAsString(); + byte[] content = TextFile.fileToBytes(f); + if (type.equals("ConceptMap")) { + System.out.println("convert "+f.getName()); + content = r5ToR4B(content); + TextFile.bytesToFile(content, f); + } +// TextFile.bytesToFile(content, Utilities.path(dest2, type+"-"+id+".json")); +// if (!set.contains(type+"/"+id)) { +// set.add(type+"/"+id); +// pck.addFile(Category.RESOURCE, type+"-"+id+".json", content); +// } + } + } + } +// pck.finish(); +// + } + + private byte[] r5ToR4B(byte[] content) throws FHIRFormatError, IOException { + try { + org.hl7.fhir.r5.model.Resource r5 = new org.hl7.fhir.r5.formats.JsonParser().parse(content); + org.hl7.fhir.r4.model.Resource r4 = VersionConvertorFactory_40_50.convertResource(r5); + return new org.hl7.fhir.r4.formats.JsonParser().composeBytes(r4); + } catch (Exception e) { + System.out.println(" .. failed: "+e.getMessage()); + return content; + } + } + +} diff --git a/org.hl7.fhir.core.generator/src/org/hl7/fhir/core/generator/engine/DefinitionsLoaderR4B.java b/org.hl7.fhir.core.generator/src/org/hl7/fhir/core/generator/engine/DefinitionsLoaderR4B.java new file mode 100644 index 000000000..cecb92e77 --- /dev/null +++ b/org.hl7.fhir.core.generator/src/org/hl7/fhir/core/generator/engine/DefinitionsLoaderR4B.java @@ -0,0 +1,87 @@ +package org.hl7.fhir.core.generator.engine; + +import java.io.IOException; + +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r4.formats.JsonParser; +import org.hl7.fhir.r4.model.Bundle; +import org.hl7.fhir.r4.model.Bundle.BundleEntryComponent; +import org.hl7.fhir.r5.model.CapabilityStatement; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.CompartmentDefinition; +import org.hl7.fhir.r5.model.ConceptMap; +import org.hl7.fhir.r5.model.OperationDefinition; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.model.SearchParameter; +import org.hl7.fhir.r5.model.StructureDefinition; +import org.hl7.fhir.r5.model.ValueSet; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.npm.NpmPackage; +import org.hl7.fhir.utilities.npm.ToolsVersion; + +public class DefinitionsLoaderR4B { + + public static Definitions load(NpmPackage npm) throws IOException { + Definitions res = new Definitions(); + + for (String t : npm.listResources("CodeSystem")) { + res.getCodeSystems().see((CodeSystem) load(npm, t), null); + } + for (String t : npm.listResources("ValueSet")) { + res.getValuesets().see((ValueSet) load(npm, t), null); + } + for (String t : npm.listResources("ConceptMap")) { + res.getConceptMaps().see((ConceptMap) load(npm, t), null); + } + for (String t : npm.listResources("CapabilityStatement")) { + res.getStatements().see((CapabilityStatement) load(npm, t), null); + } + for (String t : npm.listResources("StructureDefinition")) { + res.getStructures().see((StructureDefinition) load(npm, t), null); + } + for (String t : npm.listResources("OperationDefinition")) { + res.getOperations().see((OperationDefinition) load(npm, t), null); + } + for (String t : npm.listResources("SearchParameter")) { + res.getSearchParams().see((SearchParameter) load(npm, t), null); + } + for (String t : npm.listResources("CompartmentDefinition")) { + res.getCompartments().see((CompartmentDefinition) load(npm, t), null); + } +// Bundle bnd = (Bundle) load(npm, "Bundle-searchParams.json"); +// if (bnd != null) { +// for (BundleEntryComponent be : bnd.getEntry()) { +// Resource r = be.getResource(); +// if (r instanceof CodeSystem) { +// res.getCodeSystems().see((CodeSystem) r, null); +// } else if (r instanceof ValueSet) { +// res.getValuesets().see((ValueSet) r, null); +// } else if (r instanceof ConceptMap) { +// res.getConceptMaps().see((ConceptMap) r, null); +// } else if (r instanceof CapabilityStatement) { +// res.getStatements().see((CapabilityStatement) r, null); +// } else if (r instanceof StructureDefinition) { +// res.getStructures().see((StructureDefinition) r, null); +// } else if (r instanceof OperationDefinition) { +// res.getOperations().see((OperationDefinition) r, null); +// } else if (r instanceof SearchParameter) { +// res.getSearchParams().see((SearchParameter) r, null); +// } else if (r instanceof CompartmentDefinition) { +// res.getCompartments().see((CompartmentDefinition) r, null); +// } +// } +// } + return res; + } + + public static Resource load(NpmPackage npm, String t) { + try { + return VersionConvertorFactory_40_50.convertResource(new JsonParser().parse(npm.loadResource(t))); + } catch (Exception e) { + System.out.println("Error reading "+t+": "+e.getMessage()); + e.printStackTrace(); + return null; + } + } +} \ No newline at end of file diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java index 16f8aa443..bbdfb465a 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/utils/client/network/FhirLoggingInterceptor.java @@ -1,6 +1,8 @@ package org.hl7.fhir.r4.utils.client.network; import okhttp3.*; +import okio.Buffer; + import org.hl7.fhir.utilities.ToolingClientLogger; import javax.annotation.Nonnull; @@ -26,8 +28,17 @@ public class FhirLoggingInterceptor implements Interceptor { public Response intercept(@Nonnull Interceptor.Chain chain) throws IOException { // Log Request Request request = chain.request(); - logger.logRequest(request.method(), request.url().toString(), new ArrayList<>(request.headers().names()), - request.body() != null ? request.body().toString().getBytes() : null); + List hdrs = new ArrayList<>(); + for (String s : request.headers().toString().split("\\n")) { + hdrs.add(s.trim()); + } + byte[] cnt = null; + if (request.body() != null) { + Buffer buf = new Buffer(); + request.body().writeTo(buf); + cnt = buf.readByteArray(); + } + logger.logRequest(request.method(), request.url().toString(), hdrs, cnt); // Log Response Response response = null; diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/FhirLoggingInterceptor.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/FhirLoggingInterceptor.java index 32ed3ac72..358551756 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/FhirLoggingInterceptor.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/utils/client/network/FhirLoggingInterceptor.java @@ -1,6 +1,8 @@ package org.hl7.fhir.r4b.utils.client.network; import okhttp3.*; +import okio.Buffer; + import org.hl7.fhir.utilities.ToolingClientLogger; import javax.annotation.Nonnull; @@ -26,8 +28,17 @@ public class FhirLoggingInterceptor implements Interceptor { public Response intercept(@Nonnull Interceptor.Chain chain) throws IOException { // Log Request Request request = chain.request(); - logger.logRequest(request.method(), request.url().toString(), new ArrayList<>(request.headers().names()), - request.body() != null ? request.body().toString().getBytes() : null); + List hdrs = new ArrayList<>(); + for (String s : request.headers().toString().split("\\n")) { + hdrs.add(s.trim()); + } + byte[] cnt = null; + if (request.body() != null) { + Buffer buf = new Buffer(); + request.body().writeTo(buf); + cnt = buf.readByteArray(); + } + logger.logRequest(request.method(), request.url().toString(), hdrs, cnt); // Log Response Response response = null; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ProfileComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ProfileComparer.java index 338487903..97fb5b330 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ProfileComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/ProfileComparer.java @@ -45,14 +45,14 @@ public class ProfileComparer extends CanonicalResourceComparer { public class ProfileComparison extends CanonicalResourceComparison { - private StructuralMatch combined; + private StructuralMatch combined; public ProfileComparison(StructureDefinition left, StructureDefinition right) { super(left, right); - combined = new StructuralMatch(); // base + combined = new StructuralMatch(); // base } - public StructuralMatch getCombined() { + public StructuralMatch getCombined() { return combined; } @@ -79,7 +79,21 @@ public class ProfileComparer extends CanonicalResourceComparer { } - + private class ElementDefinitionNode { + private ElementDefinition def; + private StructureDefinition src; + private ElementDefinitionNode(StructureDefinition src, ElementDefinition def) { + super(); + this.src = src; + this.def = def; + } + public ElementDefinition getDef() { + return def; + } + public StructureDefinition getSrc() { + return src; + } + } private ProfileUtilities utilsLeft; private ProfileUtilities utilsRight; @@ -127,7 +141,7 @@ public class ProfileComparer extends CanonicalResourceComparer { if (left.getType().equals(right.getType())) { DefinitionNavigator ln = new DefinitionNavigator(session.getContextLeft(), left); DefinitionNavigator rn = new DefinitionNavigator(session.getContextRight(), right); - StructuralMatch sm = new StructuralMatch(ln.current(), rn.current()); + StructuralMatch sm = new StructuralMatch(new ElementDefinitionNode(left, ln.current()), new ElementDefinitionNode(right, rn.current())); compareElements(res, sm, ln.path(), null, ln, rn); res.combined = sm; } @@ -147,7 +161,7 @@ public class ProfileComparer extends CanonicalResourceComparer { throw new DefinitionException("StructureDefinition snapshot is empty ("+name+": "+sd.getName()+")"); } - private void compareElements(ProfileComparison comp, StructuralMatch res, String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException { + private void compareElements(ProfileComparison comp, StructuralMatch res, String path, String sliceName, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, FHIRFormatError, IOException { assert(path != null); assert(left != null); assert(right != null); @@ -190,7 +204,7 @@ public class ProfileComparer extends CanonicalResourceComparer { subset.setExample(left.current().hasExample() ? left.current().getExample() : right.current().getExample()); if (left.current().getMustSupport() != right.current().getMustSupport()) { - vm(IssueSeverity.ERROR, "Elements differ in definition for mustSupport:\r\n \""+left.current().getMustSupport()+"\"\r\n \""+right.current().getMustSupport()+"\"", path, comp.getMessages(), res.getMessages()); + vm(IssueSeverity.WARNING, "Elements differ in definition for mustSupport: '"+left.current().getMustSupport()+"' vs '"+right.current().getMustSupport()+"'", path, comp.getMessages(), res.getMessages()); } subset.setMustSupport(left.current().getMustSupport() || right.current().getMustSupport()); @@ -198,15 +212,20 @@ public class ProfileComparer extends CanonicalResourceComparer { // compare and intersect - superset.setMin(unionMin(left.current().getMin(), right.current().getMin())); - superset.setMax(unionMax(left.current().getMax(), right.current().getMax())); - subset.setMin(intersectMin(left.current().getMin(), right.current().getMin())); - subset.setMax(intersectMax(left.current().getMax(), right.current().getMax())); - rule(comp, res, subset.getMax().equals("*") || Integer.parseInt(subset.getMax()) >= subset.getMin(), path, "Cardinality Mismatch: "+card(left)+"/"+card(right)); + int leftMin = left.current().getMin(); + int rightMin = right.current().getMin(); + int leftMax = "*".equals(left.current().getMax()) ? Integer.MAX_VALUE : Integer.parseInt(left.current().getMax()); + int rightMax = "*".equals(right.current().getMax()) ? Integer.MAX_VALUE : Integer.parseInt(right.current().getMax()); + + checkMinMax(comp, res, path, leftMin, rightMin, leftMax, rightMax); + superset.setMin(unionMin(leftMin, rightMin)); + superset.setMax(unionMax(leftMax, rightMax, left.current().getMax(), right.current().getMax())); + subset.setMin(intersectMin(leftMin, rightMin)); + subset.setMax(intersectMax(leftMax, rightMax, left.current().getMax(), right.current().getMax())); superset.getType().addAll(unionTypes(comp, res, path, left.current().getType(), right.current().getType())); subset.getType().addAll(intersectTypes(comp, res, subset, path, left.current().getType(), right.current().getType())); - rule(comp, res, !subset.getType().isEmpty() || (!left.current().hasType() && !right.current().hasType()), path, "Type Mismatch:\r\n "+typeCode(left)+"\r\n "+typeCode(right)); + rule(comp, res, !subset.getType().isEmpty() || (!left.current().hasType() && !right.current().hasType()), path, "Type Mismatch: "+typeCode(left)+" vs "+typeCode(right)); // // superset.setMaxLengthElement(unionMaxLength(left.current().getMaxLength(), right.current().getMaxLength())); @@ -283,7 +302,8 @@ public class ProfileComparer extends CanonicalResourceComparer { // return null; } - private void compareChildren(ProfileComparison comp, StructuralMatch res, String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError { + + private void compareChildren(ProfileComparison comp, StructuralMatch res, String path, DefinitionNavigator left, DefinitionNavigator right) throws DefinitionException, IOException, FHIRFormatError { List lc = left.children(); List rc = right.children(); // it's possible that one of these profiles walks into a data type and the other doesn't @@ -299,10 +319,10 @@ public class ProfileComparer extends CanonicalResourceComparer { DefinitionNavigator r = findInList(rc, l); if (r == null) { comp.getUnion().getSnapshot().getElement().add(l.current().copy()); - res.getChildren().add(new StructuralMatch(l.current(), vmI(IssueSeverity.INFORMATION, "Removed this element", path))); + res.getChildren().add(new StructuralMatch(new ElementDefinitionNode(l.getStructure(), l.current()), vmI(IssueSeverity.INFORMATION, "Removed this element", path))); } else { matchR.add(r); - StructuralMatch sm = new StructuralMatch(l.current(), r.current()); + StructuralMatch sm = new StructuralMatch(new ElementDefinitionNode(l.getStructure(), l.current()), new ElementDefinitionNode(r.getStructure(), r.current())); res.getChildren().add(sm); compareElements(comp, sm, l.path(), null, l, r); } @@ -310,7 +330,7 @@ public class ProfileComparer extends CanonicalResourceComparer { for (DefinitionNavigator r : rc) { if (!matchR.contains(r)) { comp.getUnion().getSnapshot().getElement().add(r.current().copy()); - res.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this element", path), r.current())); + res.getChildren().add(new StructuralMatch(vmI(IssueSeverity.INFORMATION, "Added this element", path), new ElementDefinitionNode(r.getStructure(), r.current()))); } } } @@ -324,7 +344,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return null; } - private void ruleEqual(ProfileComparison comp, StructuralMatch res, DataType vLeft, DataType vRight, String name, String path) throws IOException { + private void ruleEqual(ProfileComparison comp, StructuralMatch res, DataType vLeft, DataType vRight, String name, String path) throws IOException { if (vLeft == null && vRight == null) { // nothing } else if (vLeft == null) { @@ -338,7 +358,7 @@ public class ProfileComparer extends CanonicalResourceComparer { private String toString(DataType val, boolean left) throws IOException { if (val instanceof PrimitiveType) - return "\"" + ((PrimitiveType) val).getValueAsString()+"\""; + return "'" + ((PrimitiveType) val).getValueAsString()+"'"; IParser jp = left ? session.getContextLeft().newJsonParser() : session.getContextRight().newJsonParser(); return jp.composeString(val, "value"); @@ -356,14 +376,14 @@ public class ProfileComparer extends CanonicalResourceComparer { return s; } - private boolean rule(ProfileComparison comp, StructuralMatch res, boolean test, String path, String message) { + private boolean rule(ProfileComparison comp, StructuralMatch res, boolean test, String path, String message) { if (!test) { vm(IssueSeverity.ERROR, message, path, comp.getMessages(), res.getMessages()); } return test; } - private String mergeText(ProfileComparison comp, StructuralMatch res, String path, String name, String left, String right, boolean isError) { + private String mergeText(ProfileComparison comp, StructuralMatch res, String path, String name, String left, String right, boolean isError) { if (left == null && right == null) return null; if (left == null) @@ -375,7 +395,7 @@ public class ProfileComparer extends CanonicalResourceComparer { if (left.equalsIgnoreCase(right)) return left; if (path != null) { - vm(isError ? IssueSeverity.ERROR : IssueSeverity.WARNING, "Elements differ in "+name+":\r\n \""+left+"\"\r\n \""+right+"\"", path, comp.getMessages(), res.getMessages()); + vm(isError ? IssueSeverity.ERROR : IssueSeverity.WARNING, "Elements differ in "+name+": '"+left+"' vs '"+right+"'", path, comp.getMessages(), res.getMessages()); } return "left: "+left+"; right: "+right; } @@ -429,6 +449,36 @@ public class ProfileComparer extends CanonicalResourceComparer { return right; } + private void checkMinMax(ProfileComparison comp, StructuralMatch res, String path, int leftMin, int rightMin, int leftMax, int rightMax) { + if (leftMin != rightMin) { + if (leftMin == 0) { + vm(IssueSeverity.INFORMATION, "Element minimum cardinalities differ: '"+leftMin+"' vs '"+rightMin+"'", path, comp.getMessages(), res.getMessages()); + } else if (rightMin == 0) { + vm(IssueSeverity.INFORMATION, "Element minimum cardinalities differ: '"+leftMin+"' vs '"+rightMin+"'", path, comp.getMessages(), res.getMessages()); + } else { + vm(IssueSeverity.INFORMATION, "Element minimum cardinalities differ: '"+leftMin+"' vs '"+rightMin+"'", path, comp.getMessages(), res.getMessages()); + } + } + if (leftMax != rightMax) { + if (leftMax == Integer.MAX_VALUE) { + vm(IssueSeverity.INFORMATION, "Element maximum cardinalities differ: '"+leftMax+"' vs '"+rightMax+"'", path, comp.getMessages(), res.getMessages()); + } else if (rightMax == Integer.MAX_VALUE) { + vm(IssueSeverity.INFORMATION, "Element maximum cardinalities differ: '"+leftMax+"' vs '"+rightMax+"'", path, comp.getMessages(), res.getMessages()); + } else { + vm(IssueSeverity.INFORMATION, "Element maximum cardinalities differ: '"+leftMax+"' vs '"+rightMax+"'", path, comp.getMessages(), res.getMessages()); + } + } +// rule(comp, res, subset.getMax().equals("*") || Integer.parseInt(subset.getMax()) >= subset.getMin(), path, "Cardinality Mismatch: "+card(left)+"/"+card(right)); + + // cross comparison - if max > min in either direction, there can be no instances that are valid against both + if (leftMax < rightMin) { + vm(IssueSeverity.ERROR, "Element minimum cardinalities conflict: '"+leftMin+".."+leftMax+"' vs '"+rightMin+".."+rightMax+"': No instances can be valid against both profiles", path, comp.getMessages(), res.getMessages()); + } + if (rightMax < leftMin) { + vm(IssueSeverity.ERROR, "Element minimum cardinalities conflict: '"+leftMin+".."+leftMax+"' vs '"+rightMin+".."+rightMax+"': No instances can be valid against both profiles", path, comp.getMessages(), res.getMessages()); + } + } + private int unionMin(int left, int right) { if (left > right) return right; @@ -436,18 +486,14 @@ public class ProfileComparer extends CanonicalResourceComparer { return left; } - private String intersectMax(String left, String right) { - int l = "*".equals(left) ? Integer.MAX_VALUE : Integer.parseInt(left); - int r = "*".equals(right) ? Integer.MAX_VALUE : Integer.parseInt(right); + private String intersectMax(int l, int r, String left, String right) { if (l < r) return left; else return right; } - private String unionMax(String left, String right) { - int l = "*".equals(left) ? Integer.MAX_VALUE : Integer.parseInt(left); - int r = "*".equals(right) ? Integer.MAX_VALUE : Integer.parseInt(right); + private String unionMax(int l, int r, String left, String right) { if (l < r) return right; else @@ -480,7 +526,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return Integer.toString(defn.current().getMin())+".."+defn.current().getMax(); } - private Collection unionTypes(ProfileComparison comp, StructuralMatch res, String path, List left, List right) throws DefinitionException, IOException, FHIRFormatError { + private Collection unionTypes(ProfileComparison comp, StructuralMatch res, String path, List left, List right) throws DefinitionException, IOException, FHIRFormatError { List result = new ArrayList(); for (TypeRefComponent l : left) checkAddTypeUnion(comp, res, path, result, l, session.getContextLeft()); @@ -489,7 +535,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return result; } - private void checkAddTypeUnion(ProfileComparison comp, StructuralMatch res, String path, List results, TypeRefComponent nw, IWorkerContext ctxt) throws DefinitionException, IOException, FHIRFormatError { + private void checkAddTypeUnion(ProfileComparison comp, StructuralMatch res, String path, List results, TypeRefComponent nw, IWorkerContext ctxt) throws DefinitionException, IOException, FHIRFormatError { boolean pfound = false; boolean tfound = false; nw = nw.copy(); @@ -586,7 +632,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return false; } - private Collection intersectTypes(ProfileComparison comp, StructuralMatch res, ElementDefinition ed, String path, List left, List right) throws DefinitionException, IOException, FHIRFormatError { + private Collection intersectTypes(ProfileComparison comp, StructuralMatch res, ElementDefinition ed, String path, List left, List right) throws DefinitionException, IOException, FHIRFormatError { List result = new ArrayList(); for (TypeRefComponent l : left) { if (l.hasAggregation()) @@ -665,7 +711,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return b.toString(); } - private boolean compareBindings(ProfileComparison comp, StructuralMatch res, ElementDefinition subset, ElementDefinition superset, String path, ElementDefinition lDef, ElementDefinition rDef) throws FHIRFormatError, DefinitionException, IOException { + private boolean compareBindings(ProfileComparison comp, StructuralMatch res, ElementDefinition subset, ElementDefinition superset, String path, ElementDefinition lDef, ElementDefinition rDef) throws FHIRFormatError, DefinitionException, IOException { assert(lDef.hasBinding() || rDef.hasBinding()); if (!lDef.hasBinding()) { subset.setBinding(rDef.getBinding()); @@ -801,7 +847,7 @@ public class ProfileComparer extends CanonicalResourceComparer { } // we can't really know about constraints. We create warnings, and collate them - private List unionConstraints(ProfileComparison comp, StructuralMatch res, String path, List left, List right) { + private List unionConstraints(ProfileComparison comp, StructuralMatch res, String path, List left, List right) { List result = new ArrayList(); for (ElementDefinitionConstraintComponent l : left) { boolean found = false; @@ -829,7 +875,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return result; } - private StructureDefinition resolveProfile(ProfileComparison comp, StructuralMatch res, String path, String url, String name, IWorkerContext ctxt) { + private StructureDefinition resolveProfile(ProfileComparison comp, StructuralMatch res, String path, String url, String name, IWorkerContext ctxt) { StructureDefinition sd = ctxt.fetchResource(StructureDefinition.class, url); if (sd == null) { ValidationMessage vm = vmI(IssueSeverity.WARNING, "Unable to resolve profile "+url+" in profile "+name, path); @@ -841,7 +887,7 @@ public class ProfileComparer extends CanonicalResourceComparer { return binding.getStrength() == BindingStrength.EXAMPLE || binding.getStrength() == BindingStrength.PREFERRED; } - private ElementDefinitionBindingComponent unionBindings(ProfileComparison comp, StructuralMatch res, String path, ElementDefinitionBindingComponent left, ElementDefinitionBindingComponent right) throws FHIRFormatError, DefinitionException, IOException { + private ElementDefinitionBindingComponent unionBindings(ProfileComparison comp, StructuralMatch res, String path, ElementDefinitionBindingComponent left, ElementDefinitionBindingComponent right) throws FHIRFormatError, DefinitionException, IOException { ElementDefinitionBindingComponent union = new ElementDefinitionBindingComponent(); if (left.getStrength().compareTo(right.getStrength()) < 0) union.setStrength(left.getStrength()); @@ -881,17 +927,17 @@ public class ProfileComparer extends CanonicalResourceComparer { return gen.generate(model, prefix, 0, null); } - private void genElementComp(String defPath, HierarchicalTableGenerator gen, List rows, StructuralMatch combined, String corePath, String prefix, Row slicingRow, boolean root) throws IOException { + private void genElementComp(String defPath, HierarchicalTableGenerator gen, List rows, StructuralMatch combined, String corePath, String prefix, Row slicingRow, boolean root) throws IOException { Row originalRow = slicingRow; Row typesRow = null; - List> children = combined.getChildren(); + List> children = combined.getChildren(); Row row = gen.new Row(); rows.add(row); - String path = combined.either().getPath(); + String path = combined.either().getDef().getPath(); row.setAnchor(path); - row.setColor(utilsRight.getRowColor(combined.either(), false)); + row.setColor(utilsRight.getRowColor(combined.either().getDef(), false)); if (eitherHasSlicing(combined)) row.setLineColor(1); else if (eitherHasSliceName(combined)) @@ -917,7 +963,7 @@ public class ProfileComparer extends CanonicalResourceComparer { row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); typesRow = row; } - } else if (combined.either().hasContentReference()) + } else if (combined.either().getDef().hasContentReference()) row.setIcon("icon_reuse.png", HierarchicalTableGenerator.TEXT_ICON_REUSE); else if (isPrimitive(combined)) row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); @@ -927,7 +973,7 @@ public class ProfileComparer extends CanonicalResourceComparer { row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); else row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); - String ref = defPath == null ? null : defPath + combined.either().getId(); + String ref = defPath == null ? null : defPath + combined.either().getDef().getId(); String sName = tail(path); String sn = getSliceName(combined); if (sn != null) @@ -937,23 +983,23 @@ public class ProfileComparer extends CanonicalResourceComparer { String leftColor = !combined.hasLeft() ? COLOR_NO_ROW_LEFT : combined.hasErrors() ? COLOR_DIFFERENT : null; String rightColor = !combined.hasRight() ? COLOR_NO_ROW_LEFT : combined.hasErrors() ? COLOR_DIFFERENT : null; if (combined.hasLeft()) { - nc = utilsRight.genElementNameCell(gen, combined.getLeft(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used , ref, sName); + nc = utilsRight.genElementNameCell(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, false, ext, used , ref, sName); } else { - nc = utilsRight.genElementNameCell(gen, combined.getRight(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used , ref, sName); + nc = utilsRight.genElementNameCell(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, false, ext, used , ref, sName); } if (combined.hasLeft()) { - frame(utilsRight.genElementCells(gen, combined.getLeft(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used , ref, sName, nc, false), leftColor); + frame(utilsRight.genElementCells(gen, combined.getLeft().getDef(), "??", true, corePath, prefix, root, false, false, combined.getLeft().getSrc(), typesRow, row, true, ext, used , ref, sName, nc, false, false), leftColor); } else { frame(spacers(row, 4, gen), leftColor); } if (combined.hasRight()) { - frame(utilsRight.genElementCells(gen, combined.getRight(), "??", true, corePath, prefix, root, false, false, null, typesRow, row, false, ext, used, ref, sName, nc, false), rightColor); + frame(utilsRight.genElementCells(gen, combined.getRight().getDef(), "??", true, corePath, prefix, root, false, false, combined.getRight().getSrc(), typesRow, row, true, ext, used, ref, sName, nc, false, false), rightColor); } else { frame(spacers(row, 4, gen), rightColor); } row.getCells().add(cellForMessages(gen, combined.getMessages())); - for (StructuralMatch child : children) { + for (StructuralMatch child : children) { genElementComp(defPath, gen, row.getSubRows(), child, corePath, prefix, originalRow, false); } } @@ -978,47 +1024,47 @@ public class ProfileComparer extends CanonicalResourceComparer { return res; } - private String getSliceName(StructuralMatch combined) { + private String getSliceName(StructuralMatch combined) { // TODO Auto-generated method stub return null; } - private boolean isDataType(StructuralMatch combined) { + private boolean isDataType(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean hasTarget(StructuralMatch combined) { + private boolean hasTarget(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean isPrimitive(StructuralMatch combined) { + private boolean isPrimitive(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean allAreReference(StructuralMatch combined) { + private boolean allAreReference(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean hasChoice(StructuralMatch combined) { + private boolean hasChoice(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean elementIsComplex(StructuralMatch combined) { + private boolean elementIsComplex(StructuralMatch combined) { // TODO Auto-generated method stub velement.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue() return false; } - private boolean eitherHasSliceName(StructuralMatch combined) { + private boolean eitherHasSliceName(StructuralMatch combined) { // TODO Auto-generated method stub return false; } - private boolean eitherHasSlicing(StructuralMatch combined) { + private boolean eitherHasSlicing(StructuralMatch combined) { // TODO Auto-generated method stub return false; } 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 92150276d..0da7d9d7e 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 @@ -301,11 +301,11 @@ public class ResourceComparer { private String halfColorForLevel(IssueSeverity level) { switch (level) { case ERROR: - return "#ffeeee"; + return "#ffdddd"; case FATAL: return "#ffcccc"; case WARNING: - return "#fff4ee"; + return "#fff6ee"; default: // INFORMATION: return "#fffff2"; } @@ -319,6 +319,9 @@ public class ResourceComparer { XhtmlNode li = new XhtmlNode(NodeType.Element, "li"); piece.getChildren().add(li); li.style("background-color: "+halfColorForLevel(msg.getLevel())); + if (msg.getLevel() == IssueSeverity.ERROR) { + li.style("font-weight: bold"); + } li.tx(msg.getMessage()); } return cell; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java index 3b231d94e..7e96e8115 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructuralMatch.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import org.hl7.fhir.r5.comparison.ResourceComparer.MessageCounts; +import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; @@ -113,5 +114,6 @@ public class StructuralMatch { return this; } + } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java index c73fdaf6f..59de6012d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/conformance/ProfileUtilities.java @@ -36,6 +36,9 @@ import java.io.FileReader; import java.io.IOException; import java.io.OutputStream; +import java.text.DateFormat; +import java.text.ParseException; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; @@ -3504,7 +3507,7 @@ public class ProfileUtilities extends TranslatingUtilities { List types = e.getType(); if (!e.hasType()) { if (root) { // we'll use base instead of types then - StructureDefinition bsd = context.fetchResource(StructureDefinition.class, profile.getBaseDefinition()); + StructureDefinition bsd = profile == null ? null : context.fetchResource(StructureDefinition.class, profile.getBaseDefinition()); if (bsd != null) { if (bsd.hasUserData("path")) { c.getPieces().add(gen.new Piece(Utilities.isAbsoluteUrl(bsd.getUserString("path")) ? bsd.getUserString("path") : imagePath +bsd.getUserString("path"), bsd.getName(), null)); @@ -3586,7 +3589,7 @@ public class ProfileUtilities extends TranslatingUtilities { c.addPiece(checkForNoChange(tl, gen.new Piece(null,", ", null))); } - ref = pkp.getLinkForProfile(profile, p.getValue()); + ref = pkp == null ? null : pkp.getLinkForProfile(profile, p.getValue()); if (ref != null) { String[] parts = ref.split("\\|"); if (parts[0].startsWith("http:") || parts[0].startsWith("https:")) { @@ -3685,7 +3688,7 @@ public class ProfileUtilities extends TranslatingUtilities { private String checkPrepend(String corePath, String path) { - if (pkp.prependLinks() && !(path.startsWith("http:") || path.startsWith("https:"))) + if (pkp != null && pkp.prependLinks() && !(path.startsWith("http:") || path.startsWith("https:"))) return corePath+path; else return path; @@ -4057,7 +4060,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (logicalModel && element.hasRepresentation(PropertyRepresentation.XMLATTR)) sName = "@"+sName; Cell nc = genElementNameCell(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName); - genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport); + genElementCells(gen, element, profileBaseFileName, snapshot, corePath, imagePath, root, logicalModel, allInvariants, profile, typesRow, row, hasDef, ext, used, ref, sName, nc, mustSupport, true); if (element.hasSlicing()) { if (standardExtensionSlicing(element)) { used.used = true; // doesn't matter whether we have a type, we're used if we're setting up slicing ... element.hasType() && element.getType().get(0).hasProfile(); @@ -4185,7 +4188,7 @@ public class ProfileUtilities extends TranslatingUtilities { public List genElementCells(HierarchicalTableGenerator gen, ElementDefinition element, String profileBaseFileName, boolean snapshot, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, StructureDefinition profile, Row typesRow, Row row, boolean hasDef, - boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport) throws IOException { + boolean ext, UnusedTracker used, String ref, String sName, Cell nameCell, boolean mustSupport, boolean allowSubRows) throws IOException { List res = new ArrayList<>(); Cell gc = gen.new Cell(); row.getCells().add(gc); @@ -4211,7 +4214,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (extDefn == null) { res.add(genCardinality(gen, element, row, hasDef, used, null)); res.add(addCell(row, gen.new Cell(null, null, "?gen-e1? "+element.getType().get(0).getProfile(), null, null))); - res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport)); + res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, profile == null ? "" : profile.getUrl(), eurl, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows)); } else { String name = urltail(eurl); nameCell.getPieces().get(0).setText(name); @@ -4224,7 +4227,7 @@ public class ProfileUtilities extends TranslatingUtilities { else // if it's complex, we just call it nothing // genTypes(gen, row, extDefn.getSnapshot().getElement().get(0), profileBaseFileName, profile); res.add(addCell(row, gen.new Cell(null, null, "("+translate("sd.table", "Complex")+")", null, null))); - res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport)); + res.add(generateDescription(gen, row, element, extDefn.getElement(), used.used, null, extDefn.getUrl(), profile, corePath, imagePath, root, logicalModel, allInvariants, valueDefn, snapshot, mustSupport, allowSubRows)); } } else { res.add(genCardinality(gen, element, row, hasDef, used, null)); @@ -4232,7 +4235,7 @@ public class ProfileUtilities extends TranslatingUtilities { res.add(addCell(row, gen.new Cell())); else res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); - res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport)); + res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows)); } } else { res.add(genCardinality(gen, element, row, hasDef, used, null)); @@ -4240,7 +4243,7 @@ public class ProfileUtilities extends TranslatingUtilities { res.add(genTypes(gen, row, element, profileBaseFileName, profile, corePath, imagePath, root, mustSupport)); else res.add(addCell(row, gen.new Cell())); - res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport)); + res.add(generateDescription(gen, row, element, (ElementDefinition) element.getUserData(DERIVATION_POINTER), used.used, null, null, profile, corePath, imagePath, root, logicalModel, allInvariants, snapshot, mustSupport, allowSubRows)); } return res; } @@ -4546,11 +4549,11 @@ public class ProfileUtilities extends TranslatingUtilities { && element.getSlicing().getRules() != SlicingRules.CLOSED && element.getSlicing().getDiscriminator().size() == 1 && element.getSlicing().getDiscriminator().get(0).getPath().equals("url") && element.getSlicing().getDiscriminator().get(0).getType().equals(DiscriminatorType.VALUE); } - private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly) throws IOException, FHIRException { - return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly); + private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows) throws IOException, FHIRException { + return generateDescription(gen, row, definition, fallback, used, baseURL, url, profile, corePath, imagePath, root, logicalModel, allInvariants, null, snapshot, mustSupportOnly, allowSubRows); } - private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly) throws IOException, FHIRException { + private Cell generateDescription(HierarchicalTableGenerator gen, Row row, ElementDefinition definition, ElementDefinition fallback, boolean used, String baseURL, String url, StructureDefinition profile, String corePath, String imagePath, boolean root, boolean logicalModel, boolean allInvariants, ElementDefinition valueDefn, boolean snapshot, boolean mustSupportOnly, boolean allowSubRows) throws IOException, FHIRException { Cell c = gen.new Cell(); row.getCells().add(c); @@ -4566,7 +4569,7 @@ public class ProfileUtilities extends TranslatingUtilities { } } if (root) { - if (profile.getAbstract()) { + if (profile != null && profile.getAbstract()) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.addPiece(gen.new Piece(null, "This is an abstract profile", null)); } @@ -4661,7 +4664,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (binding!=null && !binding.isEmpty()) { if (!c.getPieces().isEmpty()) c.addPiece(gen.new Piece("br")); - BindingResolution br = pkp.resolveBinding(profile, binding, definition.getPath()); + BindingResolution br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, binding, definition.getPath()); c.getPieces().add(checkForNoChange(binding, gen.new Piece(null, translate("sd.table", "Binding")+": ", null).addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(binding.getValueSetElement(), gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null))); if (binding.hasStrength()) { @@ -4670,7 +4673,7 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(checkForNoChange(binding.getStrengthElement(), gen.new Piece(null, ")", null))); } if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) { - br = pkp.resolveBinding(profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), definition.getPath()); + br = pkp == null ? makeNullBr(binding) : pkp.resolveBinding(profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), definition.getPath()); c.addPiece(gen.new Piece("br")); c.getPieces().add(checkForNoChange(binding, gen.new Piece(corePath+"extension-elementdefinition-maxvalueset.html", translate("sd.table", "Max Binding")+": ", "Max Value Set Extension").addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(binding, gen.new Piece(br.url == null ? null : Utilities.isAbsoluteUrl(br.url) || !pkp.prependLinks() ? br.url : corePath+br.url, br.display, null))); @@ -4706,7 +4709,7 @@ public class ProfileUtilities extends TranslatingUtilities { if (definition.hasFixed()) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, translate("sd.table", "Fixed Value")+": ", null).addStyle("font-weight:bold"))); - if (!useTableForFixedValues || definition.getFixed().isPrimitive()) { + if (!useTableForFixedValues || !allowSubRows || definition.getFixed().isPrimitive()) { String s = buildJson(definition.getFixed()); String link = null; if (Utilities.isAbsoluteUrl(s)) @@ -4714,7 +4717,7 @@ public class ProfileUtilities extends TranslatingUtilities { c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(link, s, null).addStyle("color: darkgreen"))); } else { c.getPieces().add(checkForNoChange(definition.getFixed(), gen.new Piece(null, "As shown", null).addStyle("color: darkgreen"))); - genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, false); + genFixedValue(gen, row, definition.getFixed(), snapshot, false, corePath, false); } if (isCoded(definition.getFixed()) && !hasDescription(definition.getFixed())) { Piece p = describeCoded(gen, definition.getFixed()); @@ -4724,7 +4727,7 @@ public class ProfileUtilities extends TranslatingUtilities { } else if (definition.hasPattern()) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, translate("sd.table", "Required Pattern")+": ", null).addStyle("font-weight:bold"))); - if (!useTableForFixedValues || definition.getPattern().isPrimitive()) + if (!useTableForFixedValues || !allowSubRows || definition.getPattern().isPrimitive()) c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, buildJson(definition.getPattern()), null).addStyle("color: darkgreen"))); else { c.getPieces().add(checkForNoChange(definition.getPattern(), gen.new Piece(null, "At least the following", null).addStyle("color: darkgreen"))); @@ -4764,6 +4767,13 @@ public class ProfileUtilities extends TranslatingUtilities { return c; } + private BindingResolution makeNullBr(ElementDefinitionBindingComponent binding) { + BindingResolution br = new BindingResolution(); + br.url = "http://none.none/none"; + br.display = "todo"; + return br; + } + private ElementDefinitionBindingComponent makeUnifiedBinding(ElementDefinitionBindingComponent binding, ElementDefinition element) { if (!element.hasUserData(DERIVATION_POINTER)) { return binding; @@ -4804,7 +4814,7 @@ public class ProfileUtilities extends TranslatingUtilities { ref = "?gen-fv?"; } StructureDefinition sd = context.fetchTypeDefinition(value.fhirType()); - + for (org.hl7.fhir.r5.model.Property t : value.children()) { if (t.getValues().size() > 0 || snapshot) { ElementDefinition ed = findElementDefinition(sd, t.getName()); 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 38fe4ab43..d711b9578 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 @@ -1142,7 +1142,7 @@ public abstract class BaseWorkerContext extends I18nBase implements IWorkerConte } } CodeSystem cs = fetchResource(CodeSystem.class, inc.getSystem()); - if (cs != null) { + if (cs != null && (cs.getContent() == CodeSystemContentMode.COMPLETE || cs.getContent() == CodeSystemContentMode.FRAGMENT)) { pin.addParameter().setName("tx-resource").setResource(cs); if (isTxCaching && cacheId == null || !cached.contains(cs.getVUrl())) { cached.add(cs.getVUrl()); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java index 0bffff94b..735b231a7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/ExpressionNode.java @@ -614,6 +614,9 @@ public class ExpressionNode { public String check() { + if (kind == null) { + return "Error in expression - node has no kind"; + } switch (kind) { case Name: if (Utilities.noString(name)) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java index 14b034793..a71679c06 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/ValueSetCheckerSimple.java @@ -628,6 +628,7 @@ public class ValueSetCheckerSimple extends ValueSetWorker implements ValueSetChe break; } } + i = valueset.getCompose().getInclude().size(); for (ConceptSetComponent vsi : valueset.getCompose().getExclude()) { Boolean nok = inComponent(vsi, i, system, code, valueset.getCompose().getInclude().size() == 1, warnings); i++; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java index 6f4d3ad37..60dd6958f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRLexer.java @@ -519,5 +519,8 @@ public class FHIRLexer { public int getCurrentStart() { return currentStart; } + public String getSource() { + return source; + } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java index cc9cc7b53..bcf77b7d9 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/FHIRPathEngine.java @@ -1000,7 +1000,9 @@ public class FHIRPathEngine { wrapper.setProximal(proximal); } - if (lexer.isConstant()) { + if (lexer.getCurrent() == null) { + throw lexer.error("Expression terminated unexpectedly"); + } else if (lexer.isConstant()) { boolean isString = lexer.isStringConstant(); if (!isString && (lexer.getCurrent().startsWith("-") || lexer.getCurrent().startsWith("+"))) { // the grammar says that this is a unary operation; it affects the correct processing order of the inner operations diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceUtilities.java index 0bb1b67e4..04d83ff7e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ResourceUtilities.java @@ -147,7 +147,10 @@ public class ResourceUtilities { if (cc.hasCoding("http://unstats.un.org/unsd/methods/m49/m49.htm", "001")) { return new Locale("en-US"); } - String c = cc.getCode("urn:iso:std:iso:3166:-2"); + String c = cc.getCode("urn:iso:std:iso:3166"); + if (c == null) { + return null; + } String l = jl.get(c); if (l == null) { return null; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java index da07639c6..284737538 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/ToolingExtensions.java @@ -511,7 +511,7 @@ public class ToolingExtensions { * @return The extension, if on this element, else null */ public static Extension getExtension(DomainResource resource, String name) { - if (name == null) + if (resource == null || name == null) return null; if (!resource.hasExtension()) return null; diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java index b68d58430..0bd143c86 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/client/network/FhirLoggingInterceptor.java @@ -1,6 +1,8 @@ package org.hl7.fhir.r5.utils.client.network; import okhttp3.*; +import okio.Buffer; + import org.hl7.fhir.utilities.ToolingClientLogger; import javax.annotation.Nonnull; @@ -26,8 +28,17 @@ public class FhirLoggingInterceptor implements Interceptor { public Response intercept(@Nonnull Interceptor.Chain chain) throws IOException { // Log Request Request request = chain.request(); - logger.logRequest(request.method(), request.url().toString(), new ArrayList<>(request.headers().names()), - request.body() != null ? request.body().toString().getBytes() : null); + List hdrs = new ArrayList<>(); + for (String s : request.headers().toString().split("\\n")) { + hdrs.add(s.trim()); + } + byte[] cnt = null; + if (request.body() != null) { + Buffer buf = new Buffer(); + request.body().writeTo(buf); + cnt = buf.readByteArray(); + } + logger.logRequest(request.method(), request.url().toString(), hdrs, cnt); // Log Response Response response = null; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SimpleHTTPClient.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SimpleHTTPClient.java index 318affc26..e61de56b8 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SimpleHTTPClient.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/SimpleHTTPClient.java @@ -110,7 +110,7 @@ public class SimpleHTTPClient { public HTTPResult get(String url, String accept) throws IOException { URL u = new URL(url); - boolean isSSL = url.startsWith("https://"); +// boolean isSSL = url.startsWith("https://"); // handling redirects - setInstanceFollowRedirects(true) doesn't handle crossing http to https 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 30a220e02..db22d051a 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 @@ -649,7 +649,7 @@ public class Utilities { StringBuilder s = new StringBuilder(); boolean d = false; for (String arg : args) { - if (args != null) { + if (arg != null) { if (!d) d = !noString(arg); else if (s.toString() != null && !s.toString().endsWith("/") && !arg.startsWith("/")) @@ -660,14 +660,6 @@ public class Utilities { return s.toString(); } - -// public static void checkCase(String filename) { -// File f = new CSFile(filename); -// if (!f.getName().equals(filename)) -// throw new FHIRException("Filename ") -// -// } - public static String nmtokenize(String cs) { if (cs == null) return ""; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 146063cba..a6bdae0e1 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -246,7 +246,7 @@ All_observations_should_have_a_performer = All observations should have a perfor All_observations_should_have_a_subject = All observations should have a subject Unable_to_resolve_slice_matching__no_fixed_value_or_required_value_set = Unable to resolve slice matching - no fixed value or required value set Unable_to_resolve_slice_matching__slice_matching_by_value_set_not_done = Unable to resolve slice matching - slice matching by value set not done -Problem_processing_expression__in_profile__path__ = Problem processing expression {0} in profile {1} path {2}: {3} +Problem_processing_expression__in_profile__path__ = Problem processing expression ''{0}'' in profile ''{1}'' at path ''{2}'': {3} Unable_to_find_element_with_id_ = Unable to find element with id ''{0}'' Slice_encountered_midway_through_set_path___id___ = Slice encountered midway through set (path = {0}, id = {1}); {2} Unable_to_resolve_actual_type_ = Unable to resolve actual type {0} diff --git a/org.hl7.fhir.validation.cli/pom.xml b/org.hl7.fhir.validation.cli/pom.xml index 78d411504..335380546 100644 --- a/org.hl7.fhir.validation.cli/pom.xml +++ b/org.hl7.fhir.validation.cli/pom.xml @@ -54,6 +54,10 @@ okhttp 4.9.0 + + com.atlassian.commonmark + commonmark + diff --git a/org.hl7.fhir.validation/pom.xml b/org.hl7.fhir.validation/pom.xml index bdb31ba9f..da3e72350 100644 --- a/org.hl7.fhir.validation/pom.xml +++ b/org.hl7.fhir.validation/pom.xml @@ -167,6 +167,13 @@ test + + com.atlassian.commonmark + commonmark + true + test + + io.javalin javalin diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java index 38ec59016..8101d65cf 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/InstanceValidator.java @@ -5490,7 +5490,8 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat try { n = fpe.parse(fixExpr(inv.getExpression(), inv.getKey())); } catch (FHIRLexerException e) { - throw new FHIRException(context.formatMessage(I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, inv.getExpression(), profile.getUrl(), path, e.getMessage())); + rule(errors, IssueType.INVARIANT, element.line(), element.col(), path, false, I18nConstants.PROBLEM_PROCESSING_EXPRESSION__IN_PROFILE__PATH__, inv.getExpression(), profile.getUrl(), path, e.getMessage()); + return; } timeTracker.fpe(t); inv.setUserData("validator.expression.cache", n); diff --git a/pom.xml b/pom.xml index 1d1166222..e6005c6b1 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 5.1.0 - 1.1.86 + 1.1.87-SNAPSHOT 5.7.1 1.7.1 3.0.0-M5