From 0c26f0972118f3904d7fe9fd885fa27a36bfcc6e Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Mar 2023 06:25:45 +1100 Subject: [PATCH 01/10] revise whitespace handling for unicode conformance in validator --- .../hl7/fhir/dstu2/test/TestingUtilities.java | 2 +- .../dstu2016may/test/TestingUtilities.java | 2 +- .../dstu3/test/support/TestingUtilities.java | 2 +- .../fhir/r4/test/utils/TestingUtilities.java | 2 +- .../fhir/r4b/test/utils/TestingUtilities.java | 2 +- .../conformance/profile/ProfileUtilities.java | 2 +- .../fhir/r5/test/utils/CompareUtilities.java | 3 +- .../java/org/hl7/fhir/r5/utils/FHIRLexer.java | 2 +- .../org/hl7/fhir/utilities/Utilities.java | 78 ++++++++++++++----- .../fhir/utilities/i18n/I18nConstants.java | 1 + .../utilities/xhtml/CDANarrativeFormat.java | 3 +- .../xhtml/HierarchicalTableGenerator.java | 3 +- .../src/main/resources/Messages.properties | 1 + .../org/hl7/fhir/utilities/UtilitiesTest.java | 12 +++ .../instance/InstanceValidator.java | 37 +++------ .../utils/FHIRPathExpressionFixer.java | 3 + .../conversion/tests/UtilitiesXTests.java | 3 +- 17 files changed, 102 insertions(+), 56 deletions(-) diff --git a/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/TestingUtilities.java b/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/TestingUtilities.java index b1f8b2559..bcb79ab62 100644 --- a/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/TestingUtilities.java +++ b/org.hl7.fhir.dstu2/src/test/java/org/hl7/fhir/dstu2/test/TestingUtilities.java @@ -134,7 +134,7 @@ public class TestingUtilities { } private static Node skipBlankText(Node node) { - while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) + while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isAllWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) node = node.getNextSibling(); return node; } diff --git a/org.hl7.fhir.dstu2016may/src/test/java/org/hl7/fhir/dstu2016may/test/TestingUtilities.java b/org.hl7.fhir.dstu2016may/src/test/java/org/hl7/fhir/dstu2016may/test/TestingUtilities.java index 7f3e5a065..0d6bb172f 100644 --- a/org.hl7.fhir.dstu2016may/src/test/java/org/hl7/fhir/dstu2016may/test/TestingUtilities.java +++ b/org.hl7.fhir.dstu2016may/src/test/java/org/hl7/fhir/dstu2016may/test/TestingUtilities.java @@ -134,7 +134,7 @@ public class TestingUtilities { } private static Node skipBlankText(Node node) { - while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) + while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isAllWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) node = node.getNextSibling(); return node; } diff --git a/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/support/TestingUtilities.java b/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/support/TestingUtilities.java index 4371ea1a5..860617993 100644 --- a/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/support/TestingUtilities.java +++ b/org.hl7.fhir.dstu3/src/test/java/org/hl7/fhir/dstu3/test/support/TestingUtilities.java @@ -164,7 +164,7 @@ public class TestingUtilities extends BaseTestingUtilities { } private static Node skipBlankText(Node node) { - while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) + while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isAllWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) node = node.getNextSibling(); return node; } diff --git a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java index 0a34c6e9c..d8d3d50ce 100644 --- a/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java +++ b/org.hl7.fhir.r4/src/main/java/org/hl7/fhir/r4/test/utils/TestingUtilities.java @@ -242,7 +242,7 @@ public class TestingUtilities { } private static Node skipBlankText(Node node) { - while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) + while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isAllWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) node = node.getNextSibling(); return node; } diff --git a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/test/utils/TestingUtilities.java b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/test/utils/TestingUtilities.java index b164f0cf1..cc5112fe0 100644 --- a/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/test/utils/TestingUtilities.java +++ b/org.hl7.fhir.r4b/src/main/java/org/hl7/fhir/r4b/test/utils/TestingUtilities.java @@ -257,7 +257,7 @@ public class TestingUtilities extends BaseTestingUtilities { } private static Node skipBlankText(Node node) { - while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) + while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isAllWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) node = node.getNextSibling(); return node; } 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 caf5ffa31..3864901c3 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 @@ -919,7 +919,7 @@ public class ProfileUtilities extends TranslatingUtilities { throw new FHIRException(context.formatMessage(I18nConstants.ILLEGAL_PATH__IN_DIFFERENTIAL_IN__NAME_PORTION_EXCEEDS_64_CHARS_IN_LENGTH, p, url)); } for (char ch : pp.toCharArray()) { - if (Character.isWhitespace(ch)) { + if (Utilities.isWhitespace(ch)) { throw new FHIRException(context.formatMessage(I18nConstants.ILLEGAL_PATH__IN_DIFFERENTIAL_IN__NO_UNICODE_WHITESPACE, p, url)); } if (Utilities.existsInList(ch, ',', ':', ';', '\'', '"', '/', '|', '?', '!', '@', '#', '$', '%', '^', '&', '*', '(', ')', '{', '}')) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java index 9471c138b..63c88095d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/test/utils/CompareUtilities.java @@ -1,6 +1,7 @@ package org.hl7.fhir.r5.test.utils; import org.apache.commons.codec.binary.Base64; +import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.utilities.CSFile; import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.ToolGlobalSettings; @@ -147,7 +148,7 @@ public class CompareUtilities extends BaseTestingUtilities { } private static Node skipBlankText(Node node) { - while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) + while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && StringUtils.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) node = node.getNextSibling(); return node; } 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 4d550feff..f7df5b03e 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 @@ -338,7 +338,7 @@ public class FHIRLexer { comments.add(source.substring(start, cursor).trim()); cursor = cursor + 2; } - } else if (Character.isWhitespace(source.charAt(cursor))) { + } else if (Utilities.isWhitespace(source.charAt(cursor))) { last13 = currentLocation.checkChar(source.charAt(cursor), last13); cursor++; } else { 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 e07d6b7c7..ed50e9ad6 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 @@ -826,7 +826,7 @@ public class Utilities { boolean isWhitespace = false; for (int i = 0; i < s.length(); i++) { char c = s.charAt(i); - if (!Character.isWhitespace(c)) { + if (!isWhitespace(c)) { b.append(Character.toLowerCase(c)); isWhitespace = false; } else if (!isWhitespace) { @@ -861,15 +861,6 @@ public class Utilities { } - public static boolean isWhitespace(String s) { - boolean ok = true; - for (int i = 0; i < s.length(); i++) - ok = ok && Character.isWhitespace(s.charAt(i)); - return ok; - - } - - public static String URLEncode(String string) { try { return URLEncoder.encode(string, "UTF-8"); @@ -1002,7 +993,11 @@ public class Utilities { b.append("\\\""); else if (c == '\\') b.append("\\\\"); - else if (((int) c) < 32) + else if (c == ' ') + b.append(" "); + else if (isWhitespace(c)) { + b.append("\\u"+Integer.toHexString(c)); + } else if (((int) c) < 32) b.append("\\u" + Utilities.padLeft(String.valueOf((int) c), '0', 4)); else b.append(c); @@ -1086,15 +1081,15 @@ public class Utilities { int expectedByte = in1.read(); while (expectedByte != -1) { - boolean w1 = isWhitespace(expectedByte); + boolean w1 = Character.isWhitespace(expectedByte); if (w1) - while (isWhitespace(expectedByte)) + while (Character.isWhitespace(expectedByte)) expectedByte = in1.read(); int foundByte = in2.read(); if (w1) { - if (!isWhitespace(foundByte)) + if (!Character.isWhitespace(foundByte)) return false; - while (isWhitespace(foundByte)) + while (Character.isWhitespace(foundByte)) foundByte = in2.read(); } if (expectedByte != foundByte) @@ -1121,10 +1116,6 @@ public class Utilities { } } - private static boolean isWhitespace(int b) { - return b == 9 || b == 10 || b == 13 || b == 32; - } - public static boolean compareIgnoreWhitespace(String fn1, String fn2) throws IOException { return compareIgnoreWhitespace(new File(fn1), new File(fn2)); @@ -1880,5 +1871,54 @@ public class Utilities { public static boolean isValidCRName(String name) { return name != null && name.matches("[A-Z]([A-Za-z0-9_]){1,254}"); } + + public static boolean isAllWhitespace(String s) { + if (Utilities.noString(s)) { + return true; + } + for (char ch : s.toCharArray()) { + if (!isWhitespace(ch)) { + return false; + } + } + return true; + } + + public static String trimWS(String s) { + if (Utilities.noString(s)) { + return s; + } + int start = 0; + while (start < s.length() && isWhitespace(s.charAt(start))) { + start++; + } + if (start == s.length()) { + return ""; + } + int end = s.length() - 1; + while (end >= 0 && isWhitespace(s.charAt(end))) { + end--; + } + if (start > end) { + return ""; + } + return s.substring(start, end+1); + } + + // from https://en.wikipedia.org/wiki/Whitespace_character#Unicode + public static boolean isWhitespace(int ch) { + return Utilities.existsInList(ch, '\u0009', '\n', '\u000B','\u000C','\r','\u0020','\u0085','\u00A0', + '\u1680','\u2000','\u2001','\u2002','\u2003','\u2004','\u2005','\u2006','\u2007','\u2008','\u2009','\u200A', + '\u2028', '\u2029', '\u202F', '\u205F', '\u3000'); + } + +//public static boolean !isWhitespace(String s) { +//boolean ok = true; +//for (int i = 0; i < s.length(); i++) +// ok = ok && Character.isWhitespace(s.charAt(i)); +//return ok; +// +//} + } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java index 5818006aa..f5288f393 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nConstants.java @@ -534,6 +534,7 @@ public class I18nConstants { public static final String TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS = "TYPE_SPECIFIC_CHECKS_DT_QTY_NO_ANNOTATIONS"; public static final String TYPE_SPECIFIC_CHECKS_DT_STRING_LENGTH = "Type_Specific_Checks_DT_String_Length"; public static final String TYPE_SPECIFIC_CHECKS_DT_STRING_WS = "Type_Specific_Checks_DT_String_WS"; + public static final String TYPE_SPECIFIC_CHECKS_DT_STRING_WS_ALL = "Type_Specific_Checks_DT_String_WS_ALL"; public static final String TYPE_SPECIFIC_CHECKS_DT_TIME_VALID = "Type_Specific_Checks_DT_Time_Valid"; public static final String TYPE_SPECIFIC_CHECKS_DT_URI_OID = "Type_Specific_Checks_DT_URI_OID"; public static final String TYPE_SPECIFIC_CHECKS_DT_URI_UUID = "Type_Specific_Checks_DT_URI_UUID"; diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/CDANarrativeFormat.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/CDANarrativeFormat.java index 4472b899a..da8b47459 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/CDANarrativeFormat.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/xhtml/CDANarrativeFormat.java @@ -33,6 +33,7 @@ package org.hl7.fhir.utilities.xhtml; import java.io.IOException; +import org.apache.commons.lang3.StringUtils; import org.hl7.fhir.exceptions.FHIRException; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.xml.IXMLWriter; @@ -81,7 +82,7 @@ public class CDANarrativeFormat { xn.addComment(n.getTextContent()); return; case Node.TEXT_NODE: - if (!Utilities.isWhitespace(n.getTextContent())) + if (!StringUtils.isWhitespace(n.getTextContent())) xn.addText(n.getTextContent()); return; case Node.ELEMENT_NODE: 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 ddccd0ad5..e4a6d5d9e 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 @@ -77,6 +77,7 @@ import javax.imageio.ImageIO; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.FileUtils; +import org.apache.commons.lang3.StringUtils; import org.commonmark.node.Node; import org.commonmark.parser.Parser; import org.commonmark.renderer.html.HtmlRenderer; @@ -303,7 +304,7 @@ public class HierarchicalTableGenerator extends TranslatingUtilities { myPieces.add(new Piece("br")); } if (c.getNodeType() == NodeType.Text) { - if (!Utilities.isWhitespace(c.getContent())) + if (!StringUtils.isWhitespace(c.getContent())) addNode(myPieces, c, style); } else if ("p".equals(c.getName())) { for (XhtmlNode g : c.getChildNodes()) { diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index fe96be755..7a038b115 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -218,6 +218,7 @@ Type_Specific_Checks_DT_Primitive_ValueExt = Primitive types must have a value o Type_Specific_Checks_DT_Primitive_WS = Primitive types should not only be whitespace Type_Specific_Checks_DT_String_Length = value is longer than permitted maximum length of 1 MB (1048576 bytes) Type_Specific_Checks_DT_String_WS = value should not start or finish with whitespace ''{0}'' +Type_Specific_Checks_DT_String_WS_ALL = value should not be all whitespace ''{0}'' Type_Specific_Checks_DT_Time_Valid = Not a valid time ({0}) Type_Specific_Checks_DT_URI_OID = URI values cannot start with oid: Type_Specific_Checks_DT_URI_UUID = URI values cannot start with uuid: diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java index 98732e35c..02316c1fc 100644 --- a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java @@ -229,5 +229,17 @@ class UtilitiesTest { Assertions.assertEquals(17, Utilities.getDatePrecision("1900-06-06T14:00:00.000-10:00")); } + + @Test + @DisplayName("trimWS tests") + void testTrimWS() { + Assertions.assertEquals("", Utilities.trimWS("")); + Assertions.assertEquals("", Utilities.trimWS(" ")); + Assertions.assertEquals("t", Utilities.trimWS(" t ")); + Assertions.assertEquals(".", Utilities.trimWS("\r.")); + Assertions.assertEquals("# %", Utilities.trimWS("# %")); + Assertions.assertEquals("", Utilities.trimWS("\u0009\n\u000B\u000C\r\u0020\u0085\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000")); + } + } \ No newline at end of file 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 11f75c27a..4bec122ea 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 @@ -2258,7 +2258,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_VALUEEXT) && ok; else if (e.primitiveValue().length() == 0) ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_NOTEMPTY) && ok; - else if (StringUtils.isWhitespace(e.primitiveValue())) + else if (Utilities.isAllWhitespace(e.primitiveValue())) warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.hasChildren(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_WS); if (context.hasBinding()) { ok = rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, e.line(), e.col(), path, context.getBinding().getStrength() != BindingStrength.REQUIRED, I18nConstants.Terminology_TX_Code_ValueSet_MISSING) && ok; @@ -2306,7 +2306,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat String url = e.primitiveValue(); ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("oid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URI_OID) && ok; ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !url.startsWith("uuid:"), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URI_UUID) && ok; - ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, url.equals(url.trim().replace(" ", "")) + ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, url.equals(Utilities.trimWS(url).replace(" ", "")) // work around an old invalid example in a core package || "http://www.acme.com/identifiers/patient or urn:ietf:rfc:3986 if the Identifier.value itself is a full uri".equals(url), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_URI_WS, url) && ok; ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || url.length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()) && ok; @@ -2353,7 +2353,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } if (type.equalsIgnoreCase("string") && e.hasPrimitiveValue()) { if (rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().length() > 0, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_NOTEMPTY)) { - warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || e.primitiveValue().trim().equals(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_STRING_WS, prepWSPresentation(e.primitiveValue())); + if (warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || !Utilities.isAllWhitespace(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_STRING_WS_ALL, prepWSPresentation(e.primitiveValue()))) { + warning(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue() == null || Utilities.trimWS(e.primitiveValue()).equals(e.primitiveValue()), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_STRING_WS, prepWSPresentation(e.primitiveValue())); + } if (rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, e.primitiveValue().length() <= 1048576, I18nConstants.TYPE_SPECIFIC_CHECKS_DT_STRING_LENGTH)) { ok = rule(errors, NO_RULE_DATE, IssueType.INVALID, e.line(), e.col(), path, !context.hasMaxLength() || context.getMaxLength() == 0 || e.primitiveValue().length() <= context.getMaxLength(), I18nConstants.TYPE_SPECIFIC_CHECKS_DT_PRIMITIVE_LENGTH, context.getMaxLength()) && ok; } else { @@ -2623,24 +2625,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat if (Utilities.noString(s)) { return ""; } - if (!StringUtils.containsWhitespace(s.trim())) { - return s; - } - int b = 0; - while (Character.isWhitespace(s.charAt(b))) { - b++; - } - while (!Character.isWhitespace(s.charAt(b))) { - b++; - } - int e = s.length() - 1; - while (Character.isWhitespace(s.charAt(e))) { - e--; - } - while (!Character.isWhitespace(s.charAt(e))) { - e--; - } - return s.substring(0, b)+"..."+s.substring(e+1); + return Utilities.escapeJson(s); } public boolean validateReference(ValidatorHostContext hostContext, List errors, String path, String type, ElementDefinition context, Element e, String url) { @@ -2803,7 +2788,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat boolean ok = true; for (int i = 0; i < theEncoded.length(); i++) { char nextChar = theEncoded.charAt(i); - if (Character.isWhitespace(nextChar)) { + if (Utilities.isWhitespace(nextChar)) { continue; } if (Character.isLetterOrDigit(nextChar)) { @@ -2826,7 +2811,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } for (int i = 0; i < theEncoded.length(); i++) { char nextChar = theEncoded.charAt(i); - if (Character.isWhitespace(nextChar)) { + if (Utilities.isWhitespace(nextChar)) { return true; } } @@ -2930,7 +2915,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return context.formatMessage(I18nConstants.XHTML_URL_DATA_DATA_INVALID, value); } else { if (p[0].startsWith(" ")) { - p[0] = p[0].trim(); + p[0] = Utilities.trimWS(p[0]); } String mMsg = checkValidMimeType(p[0].substring(0, p[0].lastIndexOf(";"))); if (mMsg != null) { @@ -4039,7 +4024,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } private boolean passesCodeWhitespaceRules(String v) { - if (!v.trim().equals(v)) + if (!Utilities.trimWS(v).equals(v)) return false; boolean lastWasSpace = true; for (char c : v.toCharArray()) { @@ -4048,7 +4033,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return false; else lastWasSpace = true; - } else if (Character.isWhitespace(c) || c == '\u00A0') + } else if (Utilities.isWhitespace(c)) return false; else lastWasSpace = false; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java index f27f696dc..ec1932a85 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java @@ -134,6 +134,9 @@ public class FHIRPathExpressionFixer { if (regex.equals("-?(0|[1-9][0-9]{0,17})(\\.[0-9]{1,17})?([eE][+-]?[0-9]{1,9}})?")) { return "-?(0|[1-9][0-9]{0,17})(\\.[0-9]{1,17})?([eE](0|[+\\-]?[1-9][0-9]{0,9}))?"; } + if (regex.equals("[ \\r\\n\\t\\S]+")) { + return "^[\\s\\r\\n\\t\\S]+$"; + } return regex; } diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java index b3f75fae2..6d6ff3d4e 100644 --- a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/conversion/tests/UtilitiesXTests.java @@ -45,6 +45,7 @@ import javax.xml.parsers.DocumentBuilderFactory; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; +import org.apache.commons.lang3.StringUtils; import org.fhir.ucum.UcumEssenceService; import org.hl7.fhir.convertors.loaders.loaderR5.NullLoaderKnowledgeProviderR5; import org.hl7.fhir.convertors.loaders.loaderR5.R2016MayToR5Loader; @@ -265,7 +266,7 @@ public class UtilitiesXTests { } private static Node skipBlankText(Node node) { - while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && Utilities.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) + while (node != null && (((node.getNodeType() == Node.TEXT_NODE) && StringUtils.isWhitespace(node.getTextContent())) || (node.getNodeType() == Node.COMMENT_NODE))) node = node.getNextSibling(); return node; } From 58a933094fd5773f7c037f53b7d686ae6f9fa9bd Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Mar 2023 06:30:01 +1100 Subject: [PATCH 02/10] fix json escaping --- .../src/main/java/org/hl7/fhir/utilities/Utilities.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ed50e9ad6..4459a2525 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 @@ -996,7 +996,7 @@ public class Utilities { else if (c == ' ') b.append(" "); else if (isWhitespace(c)) { - b.append("\\u"+Integer.toHexString(c)); + b.append("\\u"+Utilities.padLeft(Integer.toHexString(c), '0', 4)); } else if (((int) c) < 32) b.append("\\u" + Utilities.padLeft(String.valueOf((int) c), '0', 4)); else From 46d3faab99486b6165ec7ba6ae09b83d97f41174 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Mar 2023 20:56:45 +1100 Subject: [PATCH 03/10] patchUrls now patches fhir-type extension values --- .../fhir/convertors/loaders/loaderR5/BaseLoaderR5.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java index e3970607d..938fbf3bb 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/loaders/loaderR5/BaseLoaderR5.java @@ -17,6 +17,7 @@ import org.hl7.fhir.r5.model.StructureDefinition; import org.hl7.fhir.r5.model.UriType; import org.hl7.fhir.r5.model.ValueSet; import org.hl7.fhir.r5.model.ValueSet.ConceptSetComponent; +import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.r5.model.ElementDefinition.TypeRefComponent; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; @@ -158,7 +159,12 @@ public abstract class BaseLoaderR5 implements IContextResourceLoader { tr.setCode(URL_BASE+versionString()+"/StructureDefinition/"+tr.getCode()); } for (CanonicalType s : tr.getTargetProfile()) { - s.setValue(patchUrl(s.getValue(), "StructureDefinitino")); + s.setValue(patchUrl(s.getValue(), "StructureDefinition")); + } + if (tr.hasExtension(ToolingExtensions.EXT_FHIR_TYPE)) { + String code = ToolingExtensions.readStringExtension(tr, ToolingExtensions.EXT_FHIR_TYPE); + String url = URL_BASE+versionString()+"/StructureDefinition/"+code; + ToolingExtensions.setUrlExtension(tr, ToolingExtensions.EXT_FHIR_TYPE, url); } } if (ed.hasBinding()) { From 453f073411869efbd1653ce49f69ad4b5d7b0111 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Mar 2023 20:57:27 +1100 Subject: [PATCH 04/10] rename ig-r4.json to ig-r4.jsonx --- .../hl7/fhir/convertors/misc/NpmPackageVersionConverter.java | 2 +- .../java/org/hl7/fhir/validation/packages/PackageValidator.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java index f6432f366..86fe5ae6b 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/misc/NpmPackageVersionConverter.java @@ -84,7 +84,7 @@ public class NpmPackageVersionConverter { output.put("package/other/spec.internals", convertSpec(content.get("package/other/spec.internals"))); for (Entry e : content.entrySet()) { - if (!e.getKey().equals("package/package.json") && !e.getKey().equals("package/other/spec.internals") && !e.getKey().endsWith("ig-r4.json")) { + if (!e.getKey().equals("package/package.json") && !e.getKey().equals("package/other/spec.internals") && !e.getKey().endsWith("ig-r4.json") && !e.getKey().endsWith("ig-r4.jsonX")) { byte[] cnv = e.getValue(); try { JsonObject json = JsonParser.parseObject(e.getValue()); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java index 3887a6850..a7cb01f3b 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/packages/PackageValidator.java @@ -33,7 +33,7 @@ public class PackageValidator { NpmPackage pi = pcm.loadPackage(v.getId(), v.getVersion()); if (VersionUtilities.isR4Ver(pi.fhirVersion()) || VersionUtilities.isR3Ver(pi.fhirVersion()) || VersionUtilities.isR2Ver(pi.fhirVersion())) { for (String n : pi.list("package")) { - if (n.endsWith(".json") && !n.equals("ig-r4.json")) { + if (n.endsWith(".json") && !n.equals("ig-r4.json") && !n.equals("ig-r4.jsonX")) { InputStream s = pi.load("package", n); try { parseResource(s, pi.fhirVersion()); From bbc3cd512132200ae8753a64371fa7539b8f7032 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Mar 2023 20:58:01 +1100 Subject: [PATCH 05/10] Fix FML parsing missing locations --- .../src/main/java/org/hl7/fhir/r5/elementmodel/Element.java | 6 ++++++ .../main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java | 4 ++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java index 06b781f26..9efbe7345 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/Element.java @@ -659,6 +659,12 @@ public class Element extends Base { return this; } + public Element markLocation(Element src) { + this.line = src.line(); + this.col = src.col(); + return this; + } + public void clearDecorations() { clearUserData("fhir.decorations"); for (Element e : children) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java index a0716a98e..5bfa95387 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java @@ -361,7 +361,7 @@ public class FmlParser extends ParserBase { rule.forceElement("source").makeElement("variable").setValue(StructureMapUtilities.AUTO_VAR_NAME); rule.forceElement("target").makeElement("variable").setValue(StructureMapUtilities.AUTO_VAR_NAME); rule.forceElement("target").makeElement("transform").setValue(StructureMapTransform.CREATE.toCode()); - Element dep = rule.forceElement("dependent"); + Element dep = rule.forceElement("dependent").markLocation(rule); dep.makeElement("name").setValue(StructureMapUtilities.DEF_GROUP_NAME); dep.addElement("parameter").makeElement("valueId").setValue(StructureMapUtilities.AUTO_VAR_NAME); dep.addElement("parameter").makeElement("valueId").setValue(StructureMapUtilities.AUTO_VAR_NAME); @@ -387,7 +387,7 @@ public class FmlParser extends ParserBase { } private void parseRuleReference(Element rule, FHIRLexer lexer) throws FHIRLexerException { - Element ref = rule.addElement("dependent"); + Element ref = rule.addElement("dependent").markLocation(lexer.getCommentLocation()); ref.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take()); lexer.token("("); boolean done = false; From 6bf26255ab102fe34218cfc3f10d410aecf7977d Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Mar 2023 20:58:51 +1100 Subject: [PATCH 06/10] fix structure map validation issues around type --- .../hl7/fhir/r5/utils/ToolingExtensions.java | 20 +++++++++++++ .../src/main/resources/Messages.properties | 4 +-- .../org/hl7/fhir/utilities/UtilitiesTest.java | 25 ++++++++++++++++ .../instance/type/StructureMapValidator.java | 29 +++++++++++++++---- .../utils/FHIRPathExpressionFixer.java | 2 +- 5 files changed, 72 insertions(+), 8 deletions(-) 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 257ce1c39..cb9b7cdd1 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 @@ -629,6 +629,26 @@ public class ToolingExtensions { resource.getExtension().add(new Extension(uri).setValue(new UriType(value))); } + public static void setUrlExtension(DomainResource resource, String uri, String value) { + if (Utilities.noString(value)) + return; + Extension ext = getExtension(resource, uri); + if (ext != null) + ext.setValue(new UrlType(value)); + else + resource.getExtension().add(new Extension(uri).setValue(new UrlType(value))); + } + + public static void setUrlExtension(Element resource, String uri, String value) { + if (Utilities.noString(value)) + return; + Extension ext = getExtension(resource, uri); + if (ext != null) + ext.setValue(new UrlType(value)); + else + resource.getExtension().add(new Extension(uri).setValue(new UrlType(value))); + } + public static void setCodeExtension(DomainResource resource, String uri, String value) { if (Utilities.noString(value)) return; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 7a038b115..41e5818cb 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -883,9 +883,9 @@ SM_TARGET_TRANSLATE_BINDING_VS_TARGET = The target variable refers to an unknown SM_TARGET_TRANSLATE_BINDING_VSE_TARGET = There was an error expanding the target value set, so this concept map can''t be checked: ''{0}'' SM_TARGET_TRANSLATE_BINDING_TARGET_WRONG = The map produces one or more codes that the target value set does not include: {0} CONCEPTMAP_GROUP_SOURCE_MISSING = No Source Code System, so the source codes cannot be checked -CONCEPTMAP_GROUP_SOURCE_UNKNOWN = Unknown Source Code System, so the source codes cannot be checked +CONCEPTMAP_GROUP_SOURCE_UNKNOWN = Unknown Source Code System {0}, so the source codes cannot be checked CONCEPTMAP_GROUP_TARGET_MISSING = No Target Code System, so the source codes cannot be checked -CONCEPTMAP_GROUP_TARGET_UNKNOWN = Unknown Target Code System, so the source codes cannot be checked +CONCEPTMAP_GROUP_TARGET_UNKNOWN = Unknown Target Code System {0}, so the target codes cannot be checked CONCEPTMAP_GROUP_SOURCE_CODE_INVALID = The source code ''{0}'' is not valid in the code system {1} CONCEPTMAP_GROUP_SOURCE_DISPLAY_INVALID = The source display ''{0}'' is not valid. Possible codes {1} CONCEPTMAP_GROUP_TARGET_CODE_INVALID =The target code ''{0}'' is not valid in the code system {1} diff --git a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java index 02316c1fc..87d6e49fd 100644 --- a/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java +++ b/org.hl7.fhir.utilities/src/test/java/org/hl7/fhir/utilities/UtilitiesTest.java @@ -240,6 +240,31 @@ class UtilitiesTest { Assertions.assertEquals("# %", Utilities.trimWS("# %")); Assertions.assertEquals("", Utilities.trimWS("\u0009\n\u000B\u000C\r\u0020\u0085\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000")); } + + @Test + @DisplayName("regex tests") + void testRegex() { + Assertions.assertFalse("".matches(".+")); + Assertions.assertTrue(".".matches(".+")); + Assertions.assertTrue(" t ".matches(".+")); + Assertions.assertTrue(" ".matches(".+")); + Assertions.assertFalse("".matches("^.+$")); + Assertions.assertTrue(".".matches("^.+$")); + Assertions.assertTrue(" t ".matches("^.+$")); + Assertions.assertTrue(" ".matches("^.+$")); + Assertions.assertFalse("".matches("[\\s\\S]+")); + Assertions.assertTrue(".".matches("[\\s\\S]+")); + Assertions.assertTrue(" t ".matches("[\\s\\S]+")); + Assertions.assertTrue(" ".matches("[\\s\\S]+")); + Assertions.assertFalse("".matches("^[\\s\\S]+$")); + Assertions.assertTrue(".".matches("^[\\s\\S]+$")); + Assertions.assertTrue(" t ".matches("^[\\s\\S]+$")); + Assertions.assertTrue(" ".matches("^[\\s\\S]+$")); + Assertions.assertTrue("Example Requirements Set 2".matches("^[\\s\\S]+$")); + Assertions.assertTrue("\u0009\n\u000B\u000C\r\u0020\u0085\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000".matches("^[\\s\\S]+$")); + Assertions.assertFalse("\u0009\n\u000B\u000C\r\u0020\u0085\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000".matches(".+")); + Assertions.assertFalse("\u0009\n\u000B\u000C\r\u0020\u0085\u00A0\u1680\u2000\u2001\u2002\u2003\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u2028\u2029\u202F\u205F\u3000".matches("^.+$")); + } } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java index 89f5f0a51..9e693e29c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/StructureMapValidator.java @@ -160,6 +160,14 @@ public class StructureMapValidator extends BaseValidator { public String getWorkingType() { if (type != null) { + if (ed != null) { + for (TypeRefComponent td : ed.getType()) { + StructureDefinition sd = context.fetchTypeDefinition(td.getWorkingCode()); + if (sd != null && sd.getType().equals(type)) { + return td.getWorkingCode(); + } + } + } return type; } if (ed != null && ed.getType().size() == 1) { @@ -558,7 +566,8 @@ public class StructureMapValidator extends BaseValidator { private boolean hasType(ElementDefinition ed, String type) { for (TypeRefComponent td : ed.getType()) { - if (type.equals(td.getWorkingCode())) { + StructureDefinition sd = context.fetchTypeDefinition(td.getWorkingCode()); + if (sd != null && type.equals(sd.getType())) { return true; } } @@ -802,12 +811,12 @@ public class StructureMapValidator extends BaseValidator { // * there's a default type group for the type of the source type // otherwise, we can't know the target type. - if (ruleInfo.getDefVariable() != null && "create".equals(transform) && params.isEmpty()) { + if (ruleInfo.getDefVariable() != null && Utilities.existsInList(transform, "create", "copy") && params.isEmpty()) { VariableDefn v = variables.getVariable(ruleInfo.getDefVariable(), SOURCE); if (v != null && v.getEd() != null && (v.getEd().getType().size() == 1 || v.getType() != null)) { List dependents = rule.getChildrenByName("dependent"); if (dependents.size() == 1 && StructureMapUtilities.DEF_GROUP_NAME.equals(dependents.get(0).getChildValue("name"))) { - String type = v.getType() != null ? v.getType() : v.getEd().getTypeFirstRep().getWorkingCode(); + String type = v.getType() != null ? getTypeFromDefn(v.getEd(), v.getType()) : v.getEd().getTypeFirstRep().getWorkingCode(); // now, we look for a default group. // todo: look in this source // now look through the inputs @@ -830,6 +839,16 @@ public class StructureMapValidator extends BaseValidator { return null; } + private String getTypeFromDefn(ElementDefinition ed, String type) { + for (TypeRefComponent td : ed.getType()) { + StructureDefinition sd = context.fetchTypeDefinition(td.getWorkingCode()); + if (sd != null && type.equals(sd.getType())) { + return td.getWorkingCode(); + } + } + return type; + } + private boolean sameTypes(String type1, String type2) { if (type1 == null || type2 == null) { return false; @@ -844,11 +863,11 @@ public class StructureMapValidator extends BaseValidator { } private String getTypeForGroupInput(StructureMap map, StructureMapGroupComponent grp, StructureMapGroupInputComponent input) { - String type = input.getType(); - StructureMapModelMode mode = input.getMode() == StructureMapInputMode.SOURCE ? StructureMapModelMode.SOURCE : StructureMapModelMode.TARGET; if (input == null) { return null; } + String type = input.getType(); + StructureMapModelMode mode = input.getMode() == StructureMapInputMode.SOURCE ? StructureMapModelMode.SOURCE : StructureMapModelMode.TARGET; for (StructureMapStructureComponent st : map.getStructure()) { if (type.equals(st.getAlias()) && mode == st.getMode()) { return st.getUrl(); diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java index ec1932a85..b109c00b0 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/utils/FHIRPathExpressionFixer.java @@ -135,7 +135,7 @@ public class FHIRPathExpressionFixer { return "-?(0|[1-9][0-9]{0,17})(\\.[0-9]{1,17})?([eE](0|[+\\-]?[1-9][0-9]{0,9}))?"; } if (regex.equals("[ \\r\\n\\t\\S]+")) { - return "^[\\s\\r\\n\\t\\S]+$"; + return "^[\\s\\S]+$"; } return regex; } From e5a4d84a6f4ec3c43ff4a829d786b048951ce671 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Mar 2023 20:59:02 +1100 Subject: [PATCH 07/10] improve compact version sorting --- .../hl7/fhir/validation/cli/renderers/CompactRenderer.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/CompactRenderer.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/CompactRenderer.java index 62ec2800b..6cd1950df 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/CompactRenderer.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/CompactRenderer.java @@ -41,11 +41,11 @@ public class CompactRenderer extends ValidationOutputRenderer { String path = issue.hasExpression() ? issue.getExpression().get(0).asStringValue() : "n/a"; int line = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_LINE, -1); int col = ToolingExtensions.readIntegerExtension(issue, ToolingExtensions.EXT_ISSUE_COL, -1); - lines.add("["+Integer.toString(line) + ", " + Integer.toString(col)+"] "+path+": "+issue.getSeverity().getDisplay()+" - "+issue.getDetails().getText()); + lines.add(Utilities.padLeft(Integer.toString(line), '0', 8) + ":" + Utilities.padLeft(Integer.toString(col), '0', 8)+":"+path+"|["+Integer.toString(line) + ", " + Integer.toString(col)+"] "+path+": "+issue.getSeverity().getDisplay()+" - "+issue.getDetails().getText()); } Collections.sort(lines); for (String s : lines) { - d.println(s); + d.println(s.substring(s.indexOf("|")+1)); } } From 4486c12df5c7e86347858d7efd2ef03cb66af26d Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Mar 2023 21:47:38 +1100 Subject: [PATCH 08/10] fix location bugs in FML parsing --- .../main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java index 5bfa95387..10230cbe6 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/FmlParser.java @@ -362,9 +362,9 @@ public class FmlParser extends ParserBase { rule.forceElement("target").makeElement("variable").setValue(StructureMapUtilities.AUTO_VAR_NAME); rule.forceElement("target").makeElement("transform").setValue(StructureMapTransform.CREATE.toCode()); Element dep = rule.forceElement("dependent").markLocation(rule); - dep.makeElement("name").setValue(StructureMapUtilities.DEF_GROUP_NAME); - dep.addElement("parameter").makeElement("valueId").setValue(StructureMapUtilities.AUTO_VAR_NAME); - dep.addElement("parameter").makeElement("valueId").setValue(StructureMapUtilities.AUTO_VAR_NAME); + dep.makeElement("name").markLocation(rule).setValue(StructureMapUtilities.DEF_GROUP_NAME); + dep.addElement("parameter").markLocation(dep).makeElement("valueId").markLocation(dep).setValue(StructureMapUtilities.AUTO_VAR_NAME); + dep.addElement("parameter").markLocation(dep).makeElement("valueId").markLocation(dep).setValue(StructureMapUtilities.AUTO_VAR_NAME); // no dependencies - imply what is to be done based on types } if (newFmt) { @@ -387,7 +387,7 @@ public class FmlParser extends ParserBase { } private void parseRuleReference(Element rule, FHIRLexer lexer) throws FHIRLexerException { - Element ref = rule.addElement("dependent").markLocation(lexer.getCommentLocation()); + Element ref = rule.addElement("dependent").markLocation(lexer.getCurrentLocation()); ref.makeElement("name").markLocation(lexer.getCurrentLocation()).setValue(lexer.take()); lexer.token("("); boolean done = false; From 77e9e3a3176d88e5c38491a9594004ed3b92e131 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Mar 2023 21:47:58 +1100 Subject: [PATCH 09/10] add timestamp to compact format --- .../fhir/validation/cli/renderers/CompactRenderer.java | 3 ++- .../cli/renderers/ValidationOutputRenderer.java | 9 +++++++++ .../fhir/validation/cli/services/ValidationService.java | 6 ++++++ 3 files changed, 17 insertions(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/CompactRenderer.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/CompactRenderer.java index 6cd1950df..f15143afd 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/CompactRenderer.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/CompactRenderer.java @@ -4,6 +4,7 @@ import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.PrintStream; +import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -35,7 +36,7 @@ public class CompactRenderer extends ValidationOutputRenderer { } private void render(PrintStream d, OperationOutcome op) { - d.println(ToolingExtensions.readStringExtension(op, ToolingExtensions.EXT_OO_FILE)); + d.println(ToolingExtensions.readStringExtension(op, ToolingExtensions.EXT_OO_FILE)+" "+getRunDate()); List lines = new ArrayList<>(); for (OperationOutcome.OperationOutcomeIssueComponent issue : op.getIssue()) { String path = issue.hasExpression() ? issue.getExpression().get(0).asStringValue() : "n/a"; diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/ValidationOutputRenderer.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/ValidationOutputRenderer.java index 28a13d2bf..b8b65203c 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/ValidationOutputRenderer.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/renderers/ValidationOutputRenderer.java @@ -9,6 +9,7 @@ import org.hl7.fhir.r5.model.OperationOutcome; public abstract class ValidationOutputRenderer { + private String runDate; protected boolean crumbTrails; protected boolean moreThanOne; protected PrintStream dst; @@ -21,6 +22,14 @@ public abstract class ValidationOutputRenderer { this.crumbTrails = crumbTrails; } + public String getRunDate() { + return runDate; + } + + public void setRunDate(String runDate) { + this.runDate = runDate; + } + public void start(boolean moreThanOne) { this.moreThanOne = moreThanOne; } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java index 4fb82affb..50ff75757 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/cli/services/ValidationService.java @@ -5,8 +5,11 @@ import java.io.FileOutputStream; import java.io.PrintStream; import java.lang.management.ManagementFactory; import java.lang.management.MemoryMXBean; +import java.text.SimpleDateFormat; import java.util.ArrayList; +import java.util.Date; import java.util.List; +import java.util.Locale; import org.hl7.fhir.r5.conformance.R5ExtensionsLoader; import org.hl7.fhir.r5.context.ContextUtilities; @@ -59,9 +62,11 @@ import org.hl7.fhir.validation.cli.utils.VersionSourceInformation; public class ValidationService { private final SessionCache sessionCache; + private String runDate; public ValidationService() { sessionCache = new SessionCache(); + runDate = new SimpleDateFormat("hh:mm:ss", new Locale("en", "US")).format(new Date()); } protected ValidationService(SessionCache cache) { @@ -123,6 +128,7 @@ public class ValidationService { PrintStream dst = null; ValidationOutputRenderer renderer = makeValidationOutputRenderer(cliContext); renderer.setCrumbTrails(validator.isCrumbTrails()); + renderer.setRunDate(runDate); if (renderer.isSingleFile()) { if (cliContext.getOutput() == null) { dst = System.out; From ed016ef9ed66f19679ef73f6fa4a897f91d8219b Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Tue, 7 Mar 2023 21:51:42 +1100 Subject: [PATCH 10/10] update test case version --- pom.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pom.xml b/pom.xml index 8927ba418..866b2860b 100644 --- a/pom.xml +++ b/pom.xml @@ -19,7 +19,7 @@ 6.2.1 - 1.2.14 + 1.2.15-SNAPSHOT 5.7.1 1.8.2 3.0.0-M5