From 3f236eb88079f3f04219ff8bb99e312dcf44d883 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sun, 12 May 2024 22:27:54 +1000 Subject: [PATCH 01/14] fix rendering of multiple imports in value sets --- .../fhir/r5/renderers/ValueSetRenderer.java | 18 +++++++++++------- .../resources/rendering-phrases.properties | 3 ++- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java index a4a8d2552..ea6d37bf4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java @@ -1273,14 +1273,18 @@ public class ValueSetRenderer extends TerminologyRenderer { renderExpansionRules(li, inc, index, definitions); } } else { - li.tx(context.formatMessage(RenderingContext.VALUE_SET_IMPORT)+" "); - if (inc.getValueSet().size() < 4) { - boolean first = true; + li.tx(context.formatMessagePlural(inc.getValueSet().size(), RenderingContext.VALUE_SET_IMPORT)+" "); + if (inc.getValueSet().size() <= 2) { + int i = 0; for (UriType vs : inc.getValueSet()) { - if (first) - first = false; - else - li.tx(", "); + if (i > 0) { + if ( i < inc.getValueSet().size() - 1) { + li.tx(", "); + } else { + li.tx(" and "); + } + } + i++; XhtmlNode wli = renderStatus(vs, li); AddVsRef(vs.asStringValue(), wli, vsRes); } diff --git a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties index 83af7f3dc..760d8f394 100644 --- a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties +++ b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties @@ -620,7 +620,8 @@ VALUE_SET_SPEC_NAME = Fully specified name VALUE_SET_SYNONYM = Synonym VALUE_SET_COMMA = , VALUE_SET_WHERE_CODES = , where the codes are contained in -VALUE_SET_IMPORT = Import all the codes that are contained in +VALUE_SET_IMPORT_one = Import all the codes that are contained in +VALUE_SET_IMPORT_other = Import all the codes that are contained in the intersection of VALUE_SET_NOTE = Note: {0} VALUE_SET_ERROR = Error Expanding ValueSet: {0} VALUE_SET_NULL = null From 5ae44c23d5d815fa81f7e057665d961035b5e419 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sun, 12 May 2024 22:28:15 +1000 Subject: [PATCH 02/14] fix up LOINC validation for value set filters --- .../fhir/utilities/i18n/I18nConstants.java | 1 + .../src/main/resources/Messages.properties | 1 + .../instance/type/ValueSetValidator.java | 207 ++++++++++++++++-- 3 files changed, 191 insertions(+), 18 deletions(-) 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 fc4101e32..9165aaeda 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 @@ -1093,6 +1093,7 @@ public class I18nConstants { public static final String VALUESET_BAD_FILTER_VALUE_DECIMAL = "VALUESET_BAD_FILTER_VALUE_DECIMAL"; public static final String VALUESET_BAD_FILTER_VALUE_INTEGER = "VALUESET_BAD_FILTER_VALUE_INTEGER"; public static final String VALUESET_BAD_FILTER_VALUE_VALID_CODE = "VALUESET_BAD_FILTER_VALUE_VALID_CODE"; + public static final String VALUESET_BAD_FILTER_VALUE_VALID_CODE_LOINC = "VALUESET_BAD_FILTER_VALUE_VALID_CODE_LOINC"; public static final String VALUESET_BAD_FILTER_VALUE_CODED = "VALUESET_BAD_FILTER_VALUE_CODED"; public static final String VALUESET_BAD_FILTER_VALUE_CODED_INVALID = "VALUESET_BAD_FILTER_VALUE_CODED_INVALID"; public static final String VALUESET_BAD_FILTER_OP = "VALUESET_BAD_FILTER_OP"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index ea7bc7cc9..d0eb7a831 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -1132,6 +1132,7 @@ VALUESET_BAD_FILTER_VALUE_DATETIME = The value for a filter based on property '' VALUESET_BAD_FILTER_VALUE_DECIMAL = The value for a filter based on property ''{0}'' must be a decimal value, not ''{1}'' VALUESET_BAD_FILTER_VALUE_INTEGER = The value for a filter based on property ''{0}'' must be integer value, not ''{1}'' VALUESET_BAD_FILTER_VALUE_VALID_CODE = The value for a filter based on property ''{0}'' must be a valid code from the system ''{2}'', and ''{1}'' is not ({3}) +VALUESET_BAD_FILTER_VALUE_VALID_CODE_LOINC = The value for a filter based on property ''{0}'' looks like a LOINC code, but ''{3}'' is not valid code in LOINC version ''{1}'' VALUESET_BAD_FILTER_VALUE_CODED = The value for a filter based on property ''{0}'' must be in the format system(|version)#code, not ''{1}'' VALUESET_BAD_FILTER_VALUE_CODED_INVALID = The value for a filter based on property ''{0}'' is ''{1}'' which is not a valid code ({2}) VALUESET_BAD_FILTER_OP = The operation ''{0}'' is not allowed for property ''{1}''. Allowed ops: {2} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java index 981ea9c17..e7aa21807 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java @@ -70,6 +70,7 @@ public class ValueSetValidator extends BaseValidator { private PropertyFilterType type; private CodeValidationRule codeValidation; private EnumSet ops; + private List codeList = new ArrayList<>(); protected PropertyValidationRules(PropertyFilterType type, CodeValidationRule codeValidation, PropertyOperation... ops) { super(); @@ -96,11 +97,20 @@ public class ValueSetValidator extends BaseValidator { public CodeValidationRule getCodeValidation() { return codeValidation; } + public List getCodeList() { + return codeList; + } + public PropertyValidationRules setCodes(String... values) { + for (String v : values) { + codeList.add(v); + } + return this; + } } public enum PropertyFilterType { - Boolean, Integer, Decimal, Code, DateTime, Coding + Boolean, Integer, Decimal, Code, DateTime, Coding, CodeList, String, LoincRelationship } private static final int TOO_MANY_CODES_TO_VALIDATE = 1000; @@ -433,7 +443,7 @@ public class ValueSetValidator extends BaseValidator { } if ("exists".equals(op)) { - ok = checkFilterValue(errors, stack, system, version, ok, property, op, value, PropertyFilterType.Boolean, null) && ok; + ok = checkFilterValue(errors, stack, system, version, ok, property, op, value, new PropertyValidationRules(PropertyFilterType.Boolean, null)) && ok; } else if ("regex".equals(op)) { String err = null; try { @@ -445,10 +455,10 @@ public class ValueSetValidator extends BaseValidator { ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, !"concept".equals(property), I18nConstants.VALUESET_BAD_PROPERTY_NO_REGEX, property) && ok; } else if (Utilities.existsInList(op, "in", "not-in")) { for (String v : value.split("\\,")) { - ok = checkFilterValue(errors, stack, system, version, ok, property, op, v, rules.getType(), rules.getCodeValidation()) && ok; + ok = checkFilterValue(errors, stack, system, version, ok, property, op, v, rules) && ok; } } else { - ok = checkFilterValue(errors, stack, system, version, ok, property, op, value, rules.getType(), rules.getCodeValidation()) && ok; + ok = checkFilterValue(errors, stack, system, version, ok, property, op, value, rules) && ok; } } } @@ -474,30 +484,36 @@ public class ValueSetValidator extends BaseValidator { return false; } - private boolean checkFilterValue(List errors, NodeStack stack, String system, String version,boolean ok, String property, String op, String value, PropertyFilterType type, CodeValidationRule cr) { - if (type != null) { + private boolean checkFilterValue(List errors, NodeStack stack, String system, String version,boolean ok, String property, String op, String value, PropertyValidationRules rules) { + if (rules.getType() != null) { if (!Utilities.existsInList(op, "in", "not-in")) { - hint(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), !value.contains(","), I18nConstants.VALUESET_BAD_FILTER_VALUE_HAS_COMMA, type.toString()); + hint(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), !value.contains(","), I18nConstants.VALUESET_BAD_FILTER_VALUE_HAS_COMMA, rules.getType().toString()); } - switch (type) { + switch (rules.getType()) { case Boolean: ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, Utilities.existsInList(value, "true", "false"), I18nConstants.VALUESET_BAD_FILTER_VALUE_BOOLEAN, property, value) && ok; break; + case String: + // nothing to check + break; case Code: ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, value.trim().equals(value), I18nConstants.VALUESET_BAD_FILTER_VALUE_CODE, property, value) && ok; - if (cr == CodeValidationRule.Error || cr == CodeValidationRule.Warning) { + if (rules.getCodeValidation() == CodeValidationRule.Error || rules.getCodeValidation() == CodeValidationRule.Warning) { ValidationResult vr = context.validateCode(baseOptions, system, version, value, null); - if (cr == CodeValidationRule.Error) { + if (rules.getCodeValidation() == CodeValidationRule.Error) { ok = rule(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE, property, value, system, vr.getMessage()) && ok; } else { warning(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE, property, value, system, vr.getMessage()); } } break; + case CodeList: + ok = rule(errors, "2024-05-12", IssueType.INVALID, stack.getLiteralPath(), rules.getCodeList().contains(value), I18nConstants.VALUESET_BAD_FILTER_VALUE_DATETIME, property, value) && ok; + break; case DateTime: ok = rule(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), value.matches("([0-9]([0-9]([0-9][1-9]|[1-9]0)|[1-9]00)|[1-9]000)(-(0[1-9]|1[0-2])(-(0[1-9]|[1-2][0-9]|3[0-1])(T([01][0-9]|2[0-3]):[0-5][0-9]:([0-5][0-9]|60)(\\.[0-9]+)?(Z|(\\+|-)((0[0-9]|1[0-3]):[0-5][0-9]|14:00))?)?)?)?"), @@ -522,6 +538,15 @@ public class ValueSetValidator extends BaseValidator { ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_CODED_INVALID, property, value, vr.getMessage()) && ok; } break; + case LoincRelationship: + // see https://chat.fhir.org/#narrow/stream/179202-terminology/topic/LOINC.20properties.20in.20filters + // for now, the value can be a LOINC code, or a string value + if (value.matches("(L[A|L|P])?(\\d)+\\-\\d")) { + ValidationResult vr = context.validateCode(baseOptions, system, version, value, null); + ok = rule(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE_LOINC, property, value, system, vr.getMessage()) && ok; + } else { + // nothing? + } default: break; } @@ -581,13 +606,7 @@ public class ValueSetValidator extends BaseValidator { } switch (system) { case "http://loinc.org" : - if (Utilities.existsInList(property, "copyright", "STATUS", "CLASS", "CONSUMER_NAME", "ORDER_OBS", "DOCUMENT_SECTION", "SCALE_TYP")) { - return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None); - } else if ("CLASSTYPE".equals(property)) { - return new PropertyValidationRules(PropertyFilterType.Integer, null, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In)); - } else { - return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In)); - } + return getLOINCPropertyDetails(property, ops); case "http://snomed.info/sct": switch (property) { case "constraint": return null; // for now @@ -615,6 +634,93 @@ public class ValueSetValidator extends BaseValidator { } + private PropertyValidationRules getLOINCPropertyDetails(String property, EnumSet ops) { + if (Utilities.existsInList(property, + "parent", + "child", + "answers-for", + "TIME MODIFIER", + "TIME_ASPCT", + "SYSTEM", + "SUPER SYSTEM", + "SUFFIX", + "SCALE", + "SCALE_TYP", + "Rad.View.View Type", + "Rad.View.Aggregation", + "Rad.Timing", + "Rad.Subject", + "Rad.Reason for Exam", + "Rad.Pharmaceutical.Substance Given", + "Rad.Pharmaceutical.Route", + "Rad.Modality.Modality Type", + "Rad.Modality.Modality Subtype", + "Rad.Maneuver.Maneuver Type", + "Rad.Guidance for.Presence", + "Rad.Guidance for.Object", + "Rad.Guidance for.Approach", + "Rad.Guidance for.Action", + "Rad.Anatomic Location.Region Imaged", + "Rad.Anatomic Location.Laterality.Presence", + "Rad.Anatomic Location.Laterality", + "Rad.Anatomic Location.Imaging Focus", + "PROPERTY", + "METHOD", + "GENE", + "Document.TypeOfService", + "Document.SubjectMatterDomain", + "Document.Setting", + "Document.Role", + "Document.Kind", + "DIVISORS", + "COUNT", + "COMPONENT", + "CLASS", + "CHALLENGE", + "AnswerList", + "answer-list", + "Answer", + "ADJUSTMENT")) { + return new PropertyValidationRules(PropertyFilterType.LoincRelationship, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)); + } + + if (Utilities.existsInList(property, + "UNITSREQUIRED", + "PanelType", + "ORDER_OBS", + "EXAMPLE_UNITS", + "EXAMPLE_UCUM_UNITS", + "Copyright", + "CLASSTYPE", + "CLASS", + "AskAtOrderEntry")) { + return new PropertyValidationRules(PropertyFilterType.String, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)); + } + + if (Utilities.existsInList(property, + "STATUS")) { + return new PropertyValidationRules(PropertyFilterType.CodeList, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)) + .setCodes("ACTIVE", "DEPRECATED", "DISCOURAGED", "DocumentOntology", "EXAMPLE", "NORMATIVE", "NotStated", "PREFERRED", "Primary", "Radiology", "TRIAL"); + } + + if (Utilities.existsInList(property, + "LIST", + "ancestor")) { + return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)); + } + + if (Utilities.existsInList(property, + "copyright")) { + return new PropertyValidationRules(PropertyFilterType.CodeList, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)).setCodes("LOINC", "3rdParty"); + } + + if (Utilities.existsInList(property, + "concept")) { + return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, addToOps(ops, PropertyOperation.IsA)); + } + return null; + } + private EnumSet addToOps(EnumSet set, PropertyOperation... ops) { for (PropertyOperation op : ops) { @@ -648,7 +754,72 @@ public class ValueSetValidator extends BaseValidator { private String[] getSystemKnownNames(String system) { switch (system) { - case "http://loinc.org" : return new String[] {"parent", "ancestor", "copyright", "STATUS", "COMPONENT", "PROPERTY", "TIME_ASPCT", "SYSTEM", "SCALE_TYP", "METHOD_TYP", "CLASS", "CONSUMER_NAME", "CLASSTYPE", "ORDER_OBS", "DOCUMENT_SECTION"}; + case "http://loinc.org" : return new String[] { + // = and regex + + "parent", + "child", + "answers-for", + "TIME MODIFIER", + "TIME_ASPCT", + "SYSTEM", + "SUPER SYSTEM", + "SUFFIX", + "SCALE", + "SCALE_TYP", + "Rad.View.View Type", + "Rad.View.Aggregation", + "Rad.Timing", + "Rad.Subject", + "Rad.Reason for Exam", + "Rad.Pharmaceutical.Substance Given", + "Rad.Pharmaceutical.Route", + "Rad.Modality.Modality Type", + "Rad.Modality.Modality Subtype", + "Rad.Maneuver.Maneuver Type", + "Rad.Guidance for.Presence", + "Rad.Guidance for.Object", + "Rad.Guidance for.Approach", + "Rad.Guidance for.Action", + "Rad.Anatomic Location.Region Imaged", + "Rad.Anatomic Location.Laterality.Presence", + "Rad.Anatomic Location.Laterality", + "Rad.Anatomic Location.Imaging Focus", + "PROPERTY", + "METHOD", + "GENE", + "Document.TypeOfService", + "Document.SubjectMatterDomain", + "Document.Setting", + "Document.Role", + "Document.Kind", + "DIVISORS", + "COUNT", + "COMPONENT", + "CLASS", + "CHALLENGE", + "AnswerList", + "answer-list", + "Answer", + "ADJUSTMENT", + "UNITSREQUIRED", + "PanelType", + "ORDER_OBS", + "EXAMPLE_UNITS", + "EXAMPLE_UCUM_UNITS", + "Copyright", + "CLASSTYPE", + "CLASS", + "AskAtOrderEntry", + "ancestor", + + // just equals + "STATUS", + "LIST", + "copyright", + +// == is-a on: + "concept"}; case "http://snomed.info/sct": return new String[] { "constraint", "expressions", "410662002", "42752001", "47429007", "116676008", "116686009", "118168003", "118169006", "118170007", "118171006", "127489000", "131195008", "246075003", "246090004", "246093002", "246112005", "246454002", "246456000", "246501002", "246513007", "246514001", "255234002", "260507000", From 1e3bc60d6e60d1861de9a0719d560f98c9f1dfa7 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sun, 12 May 2024 23:24:20 +1000 Subject: [PATCH 03/14] Fix up translation issues --- .../fhir/r5/elementmodel/LanguageUtils.java | 128 ++++++++++++++---- .../utilities/i18n/JsonLangFileProducer.java | 3 + .../utilities/i18n/LanguageFileProducer.java | 11 ++ .../utilities/i18n/PoGetTextProducer.java | 3 + pom.xml | 2 +- 5 files changed, 120 insertions(+), 27 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/LanguageUtils.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/LanguageUtils.java index b8f4cd014..d900a5fba 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/LanguageUtils.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/elementmodel/LanguageUtils.java @@ -19,6 +19,8 @@ import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.ElementDefinition; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; +import org.hl7.fhir.r5.model.Extension; +import org.hl7.fhir.r5.model.MarkdownType; import org.hl7.fhir.r5.model.Resource; import org.hl7.fhir.r5.model.StringType; import org.hl7.fhir.r5.model.StructureDefinition; @@ -56,8 +58,9 @@ public class LanguageUtils { private static final String ORPHAN_TRANSLATIONS_NAME = "translations.orphans"; - private static final String SUPPLEMENT_NAME = "translations.supplement"; - + private static final String SUPPLEMENT_SOURCE_RESOURCE = "translations.supplemented"; + private static final String SUPPLEMENT_SOURCE_TRANSLATIONS = "translations.source-list"; + IWorkerContext context; private List crlist; @@ -332,18 +335,63 @@ public class LanguageUtils { return dstLang == null || srcLang == null ? false : dstLang.startsWith(srcLang) || "*".equals(srcLang); } - public static void fillSupplement(CodeSystem cs, List list) { - cs.setUserData(SUPPLEMENT_NAME, "true"); + public static void fillSupplement(CodeSystem csSrc, CodeSystem csDst, List list) { + csDst.setUserData(SUPPLEMENT_SOURCE_RESOURCE, csSrc); + csDst.setUserData(SUPPLEMENT_SOURCE_TRANSLATIONS, list); for (TranslationUnit tu : list) { - ConceptDefinitionComponent cd = CodeSystemUtilities.getCode(cs, tu.getId()); - if (cd != null && cd.hasDisplay() && cd.getDisplay().equals(tu.getSrcText())) { - cd.addDesignation().setLanguage(tu.getLanguage()).setValue(tu.getTgtText()); - } else { - addOrphanTranslation(cs, tu); + String code = tu.getId(); + String subCode = null; + if (code.contains("@")) { + subCode = code.substring(code.indexOf("@")+1); + code = code.substring(0, code.indexOf("@")); } + ConceptDefinitionComponent cdSrc = CodeSystemUtilities.getCode(csSrc, tu.getId()); + if (cdSrc == null) { + addOrphanTranslation(csSrc, tu); + } else { + ConceptDefinitionComponent cdDst = CodeSystemUtilities.getCode(csDst, cdSrc.getCode()); + if (cdDst == null) { + cdDst = csDst.addConcept().setCode(cdSrc.getCode()); + } + String tt = tu.getTgtText(); + if (tt.startsWith("!!")) { + tt = tt.substring(3); + } + if (subCode == null) { + cdDst.setDisplay(tt); + } else if ("definition".equals(subCode)) { + cdDst.setDefinition(tt); + } else { + boolean found = false; + for (ConceptDefinitionDesignationComponent d : cdSrc.getDesignation()) { + if (d.hasUse() && subCode.equals(d.getUse().getCode())) { + found = true; + cdDst.addDesignation().setUse(d.getUse()).setLanguage(tu.getLanguage()).setValue(tt); //.setUserData(SUPPLEMENT_SOURCE, tu); + break; + } + } + if (!found) { + for (Extension e : cdSrc.getExtension()) { + if (subCode.equals(tail(e.getUrl()))) { + found = true; + cdDst.addExtension().setUrl(e.getUrl()).setValue( + e.getValue().fhirType().equals("markdown") ? new MarkdownType(tt) : new StringType(tt)); //.setUserData(SUPPLEMENT_SOURCE, tu); + break; + } + } + } + if (!found) { + addOrphanTranslation(csSrc, tu); + } + } + } } } + private static Object tail(String url) { + return url.contains("/") ? url.substring(url.lastIndexOf("/")+1) : url; + } + private static void addOrphanTranslation(CodeSystem cs, TranslationUnit tu) { List list = (List) cs.getUserData(ORPHAN_TRANSLATIONS_NAME); if (list == null) { @@ -376,7 +424,7 @@ public class LanguageUtils { } public static boolean handlesAsResource(Resource resource) { - return (resource instanceof CodeSystem && resource.hasUserData(SUPPLEMENT_NAME)) || (resource instanceof StructureDefinition); + return (resource instanceof CodeSystem && resource.hasUserData(SUPPLEMENT_SOURCE_RESOURCE)) || (resource instanceof StructureDefinition); } public static boolean handlesAsElement(Element element) { @@ -388,10 +436,23 @@ public class LanguageUtils { if (res instanceof StructureDefinition) { StructureDefinition sd = (StructureDefinition) res; generateTranslations(list, sd, lang); + if (res.hasUserData(ORPHAN_TRANSLATIONS_NAME)) { + List orphans = (List) res.getUserData(ORPHAN_TRANSLATIONS_NAME); + for (TranslationUnit t : orphans) { + list.add(new TranslationUnit(lang, "!!"+t.getId(), t.getContext1(), t.getSrcText(), t.getTgtText())); + } + } } else { - CodeSystem cs = (CodeSystem) res; + CodeSystem cs = (CodeSystem) res.getUserData(SUPPLEMENT_SOURCE_RESOURCE); + List inputs = res.hasUserData(SUPPLEMENT_SOURCE_TRANSLATIONS) ? (List) res.getUserData(SUPPLEMENT_SOURCE_TRANSLATIONS) : new ArrayList<>(); for (ConceptDefinitionComponent cd : cs.getConcept()) { - generateTranslations(list, cd, lang); + generateTranslations(list, cd, lang, inputs); + } + if (cs.hasUserData(ORPHAN_TRANSLATIONS_NAME)) { + List orphans = (List) cs.getUserData(ORPHAN_TRANSLATIONS_NAME); + for (TranslationUnit t : orphans) { + list.add(new TranslationUnit(lang, "!!"+t.getId(), t.getContext1(), t.getSrcText(), t.getTgtText())); + } } } return list; @@ -434,26 +495,41 @@ public class LanguageUtils { } - private static void generateTranslations(List list, ConceptDefinitionComponent cd, String lang) { - String code = cd.getCode(); - String display = cd.getDisplay(); - String target = null; - for (ConceptDefinitionDesignationComponent d : cd.getDesignation()) { - if (target == null && !d.hasUse() && d.hasLanguage() && lang.equals(d.getLanguage())) { - target = d.getValue(); - } + private static void generateTranslations(List list, ConceptDefinitionComponent cd, String lang, List inputs) { + // we generate translation units for the display, the definition, and any designations and extensions that we find + // the id of the designation is the use.code (there will be a use) and for the extension, the tail of the extension URL + // todo: do we need to worry about name clashes? why would we, and more importantly, how would we solve that? + + addTranslationUnit(list, cd.getCode(), cd.getDisplay(), lang, inputs); + if (cd.hasDefinition()) { + addTranslationUnit(list, cd.getCode()+"@definition", cd.getDefinition(), lang, inputs); } for (ConceptDefinitionDesignationComponent d : cd.getDesignation()) { - if (target == null && d.hasLanguage() && lang.equals(d.getLanguage())) { - target = d.getValue(); - } + addTranslationUnit(list, cd.getCode()+"@"+d.getUse().getCode(), d.getValue(), lang, inputs); } - list.add(new TranslationUnit(lang, code, getDefinition(cd), display, target)); - for (ConceptDefinitionComponent cd1 : cd.getConcept()) { - generateTranslations(list, cd1, lang); + for (Extension e : cd.getExtension()) { + addTranslationUnit(list, cd.getCode()+"@"+tail(e.getUrl()), e.getValue().primitiveValue(), lang, inputs); } } + private static void addTranslationUnit(List list, String id, String srcText, String lang, List inputs) { + TranslationUnit existing = null; + for (TranslationUnit t : inputs) { + if (id.equals(t.getId())) { + existing = t; + break; + } + } + // not sure what to do with context? + if (existing == null) { + list.add(new TranslationUnit(lang, id, null, srcText, null)); + } else if (srcText.equals(existing.getSrcText())) { + list.add(new TranslationUnit(lang, id, null, srcText, existing.getTgtText())); + } else { + list.add(new TranslationUnit(lang, id, null, srcText, "!!"+existing.getTgtText()).setOriginal(existing.getSrcText())); + } + } + private static String getDefinition(ConceptDefinitionComponent cd) { ConceptPropertyComponent v = CodeSystemUtilities.getProperty(cd, "translation-context"); if (v != null && v.hasValue()) { diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/JsonLangFileProducer.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/JsonLangFileProducer.java index ddd6ce42e..fae406088 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/JsonLangFileProducer.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/JsonLangFileProducer.java @@ -130,6 +130,9 @@ public class JsonLangFileProducer extends LanguageFileProducer { if (tu.getContext1() != null) { entry.add("context", tu.getContext1()); } + if (tu.getOriginal() != null) { + entry.add("original", tu.getOriginal()); + } entry.add("source", tu.getSrcText()); entry.add("target", tu.getTgtText()); } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/LanguageFileProducer.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/LanguageFileProducer.java index 4af92f8ae..1375615c6 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/LanguageFileProducer.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/LanguageFileProducer.java @@ -65,6 +65,7 @@ public abstract class LanguageFileProducer { public static class TranslationUnit extends TextUnit { private String language; + private String original; // for when the source text has been modified since being translated public TranslationUnit(String language, String id, String context, String srcText, String tgtText) { super(id, context, srcText, tgtText); @@ -86,6 +87,16 @@ public abstract class LanguageFileProducer { public void setTgtText(String tgtText) { this.tgtText = tgtText; } + + public String getOriginal() { + return original; + } + + public TranslationUnit setOriginal(String original) { + this.original = original; + return this; + } + } public class Translations { diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/PoGetTextProducer.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/PoGetTextProducer.java index 266417d9e..4b3f67294 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/PoGetTextProducer.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/PoGetTextProducer.java @@ -167,6 +167,9 @@ public class PoGetTextProducer extends LanguageFileProducer { if (tu.getContext1() != null) { ln(po, "#. "+tu.getContext1()); } + if (tu.getOriginal() != null) { + ln(po, "#| "+tu.getOriginal()); + } ln(po, "msgid \""+stripEoln(tu.getSrcText())+"\""); ln(po, "msgstr \""+(tu.getTgtText() == null ? "" : stripEoln(tu.getTgtText()))+"\""); ln(po, ""); diff --git a/pom.xml b/pom.xml index df6326f1c..8316373e6 100644 --- a/pom.xml +++ b/pom.xml @@ -21,7 +21,7 @@ 1.26.0 32.0.1-jre 6.4.1 - 1.5.7 + 1.5.8-SNAPSHOT 2.17.0 5.9.2 1.8.2 From a32d6efc49d032d5cef25753aa7a5d6238892565 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 18 May 2024 03:22:28 -0500 Subject: [PATCH 04/14] fixes for AdditionalBinding version conversion in profiles --- .../ElementDefinition30_50.java | 70 ++++++++++++++++- .../special40_50/ElementDefinition40_50.java | 74 +++++++++++++++++- .../special43_50/ElementDefinition43_50.java | 75 ++++++++++++++++++- 3 files changed, 215 insertions(+), 4 deletions(-) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java index 27f33cd02..256ff156e 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java @@ -5,6 +5,7 @@ import java.util.stream.Collectors; import org.hl7.fhir.convertors.VersionConvertorConstants; import org.hl7.fhir.convertors.context.ConversionContext30_50; +import org.hl7.fhir.convertors.context.ConversionContext30_50; import org.hl7.fhir.convertors.conv30_50.VersionConvertor_30_50; import org.hl7.fhir.convertors.conv30_50.datatypes30_50.complextypes30_50.Coding30_50; import org.hl7.fhir.convertors.conv30_50.datatypes30_50.primitivetypes30_50.Boolean30_50; @@ -17,9 +18,17 @@ import org.hl7.fhir.convertors.conv30_50.datatypes30_50.primitivetypes30_50.Unsi import org.hl7.fhir.convertors.conv30_50.datatypes30_50.primitivetypes30_50.Uri30_50; import org.hl7.fhir.convertors.conv30_50.resources30_50.Enumerations30_50; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.dstu3.model.BooleanType; +import org.hl7.fhir.dstu3.model.Extension; +import org.hl7.fhir.dstu3.model.MarkdownType; +import org.hl7.fhir.dstu3.model.StringType; +import org.hl7.fhir.dstu3.model.UriType; import org.hl7.fhir.r5.model.CanonicalType; +import org.hl7.fhir.r5.model.CodeType; import org.hl7.fhir.r5.model.DataType; import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.UsageContext; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; import org.hl7.fhir.utilities.Utilities; public class ElementDefinition30_50 { @@ -643,7 +652,8 @@ public class ElementDefinition30_50 { public static ElementDefinition.ElementDefinitionBindingComponent convertElementDefinitionBindingComponent(org.hl7.fhir.dstu3.model.ElementDefinition.ElementDefinitionBindingComponent src) throws FHIRException { if (src == null) return null; ElementDefinition.ElementDefinitionBindingComponent tgt = new ElementDefinition.ElementDefinitionBindingComponent(); - ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().copyElement(src, tgt, VersionConvertor_30_50.EXT_SRC_TYPE); + ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().copyElement(src, tgt, VersionConvertor_30_50.EXT_SRC_TYPE, + "http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional"); if (src.hasStrength()) tgt.setStrengthElement(Enumerations30_50.convertBindingStrength(src.getStrengthElement())); if (src.hasDescription()) tgt.setDescriptionElement(String30_50.convertStringToMarkdown(src.getDescriptionElement())); if (src.hasValueSet()) { @@ -657,6 +667,9 @@ public class ElementDefinition30_50 { } tgt.setValueSet(VersionConvertorConstants.refToVS(tgt.getValueSet())); } + for (org.hl7.fhir.dstu3.model.Extension ext : src.getExtensionsByUrl("http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional")) { + tgt.addAdditional(convertAdditional(ext)); + } return tgt; } @@ -680,9 +693,64 @@ public class ElementDefinition30_50 { else tgt.setValueSet(new org.hl7.fhir.dstu3.model.Reference(src.getValueSet())); } } + for (ElementDefinitionBindingAdditionalComponent ab : src.getAdditional()) { + tgt.addExtension(convertAdditional(ab)); + } return tgt; } + + private static ElementDefinitionBindingAdditionalComponent convertAdditional(Extension src) { + if (src == null) return null; + ElementDefinitionBindingAdditionalComponent tgt = new ElementDefinitionBindingAdditionalComponent(); + ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().copyElement(src, tgt, "valueSet", "purpose", "documentation", "shortDoco", "usage", "any"); + if (src.hasExtension("purpose")) { + tgt.getPurposeElement().setValueAsString(src.getExtensionByUrl("purpose").getValue().primitiveValue()); + } + if (src.hasExtension("valueSet")) { + tgt.setValueSetElement(Uri30_50.convertCanonical((UriType) src.getExtensionByUrl("valueSet").getValue())); + } + if (src.hasExtension("documentation")) { + tgt.setDocumentationElement(MarkDown30_50.convertMarkdown((MarkdownType) src.getExtensionByUrl("documentation").getValue())); + } + if (src.hasExtension("shortDoco")) { + tgt.setShortDocoElement(String30_50.convertString((StringType) src.getExtensionByUrl("shortDoco").getValue())); + } + for (Extension t : src.getExtensionsByUrl("usage")) { + tgt.addUsage(UsageContext30_50.convertUsageContext((org.hl7.fhir.dstu3.model.UsageContext) t.getValue())); + } + if (src.hasExtension("any")) { + tgt.setAnyElement(Boolean30_50.convertBoolean((BooleanType) src.getExtensionByUrl("any").getValue())); + } + return tgt; + } + + private static Extension convertAdditional(ElementDefinitionBindingAdditionalComponent src) { + if (src == null) return null; + Extension tgt = new Extension(); + ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().copyElement(src, tgt); + if (src.hasPurpose()) { + tgt.addExtension(new Extension("purpose", new CodeType(src.getPurposeElement().primitiveValue()))); + } + if (src.hasValueSet()) { + tgt.addExtension(new Extension("valueSet", Uri30_50.convertCanonical(src.getValueSetElement()))); + } + if (src.hasDocumentation()) { + tgt.addExtension(new Extension("documentation", MarkDown30_50.convertMarkdown(src.getDocumentationElement()))); + } + if (src.hasShortDoco()) { + tgt.addExtension(new Extension("shortDoco", String30_50.convertString(src.getShortDocoElement()))); + } + for (UsageContext t : src.getUsage()) { + tgt.addExtension(new Extension("usage", UsageContext30_50.convertUsageContext(t))); + } + if (src.hasAny()) { + tgt.addExtension(new Extension("any", Boolean30_50.convertBoolean(src.getAnyElement()))); + } + + return tgt; + } + public static ElementDefinition.ElementDefinitionMappingComponent convertElementDefinitionMappingComponent(org.hl7.fhir.dstu3.model.ElementDefinition.ElementDefinitionMappingComponent src) throws FHIRException { if (src == null) return null; ElementDefinition.ElementDefinitionMappingComponent tgt = new ElementDefinition.ElementDefinitionMappingComponent(); diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/datatypes40_50/special40_50/ElementDefinition40_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/datatypes40_50/special40_50/ElementDefinition40_50.java index c46fcd586..f51e9f55f 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/datatypes40_50/special40_50/ElementDefinition40_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv40_50/datatypes40_50/special40_50/ElementDefinition40_50.java @@ -5,6 +5,7 @@ import java.util.stream.Collectors; import org.hl7.fhir.convertors.context.ConversionContext40_50; import org.hl7.fhir.convertors.conv40_50.datatypes40_50.BackboneElement40_50; import org.hl7.fhir.convertors.conv40_50.datatypes40_50.general40_50.Coding40_50; +import org.hl7.fhir.convertors.conv40_50.datatypes40_50.metadata40_50.UsageContext40_50; import org.hl7.fhir.convertors.conv40_50.datatypes40_50.primitive40_50.Boolean40_50; import org.hl7.fhir.convertors.conv40_50.datatypes40_50.primitive40_50.Canonical40_50; import org.hl7.fhir.convertors.conv40_50.datatypes40_50.primitive40_50.Code40_50; @@ -16,12 +17,22 @@ import org.hl7.fhir.convertors.conv40_50.datatypes40_50.primitive40_50.UnsignedI import org.hl7.fhir.convertors.conv40_50.datatypes40_50.primitive40_50.Uri40_50; import org.hl7.fhir.convertors.conv40_50.resources40_50.Enumerations40_50; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r4.model.BooleanType; +import org.hl7.fhir.r4.model.CanonicalType; +import org.hl7.fhir.r4.model.Extension; +import org.hl7.fhir.r4.model.MarkdownType; +import org.hl7.fhir.r4.model.StringType; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; +import org.hl7.fhir.r5.model.UsageContext; public class ElementDefinition40_50 { public static org.hl7.fhir.r5.model.ElementDefinition convertElementDefinition(org.hl7.fhir.r4.model.ElementDefinition src) throws FHIRException { if (src == null) return null; org.hl7.fhir.r5.model.ElementDefinition tgt = new org.hl7.fhir.r5.model.ElementDefinition(); - BackboneElement40_50.copyBackboneElement(src, tgt, "http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.mustHaveValue", "http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.valueAlternatives"); + BackboneElement40_50.copyBackboneElement(src, tgt, + "http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.mustHaveValue", + "http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.valueAlternatives"); if (src.hasPath()) tgt.setPathElement(String40_50.convertString(src.getPathElement())); tgt.setRepresentation(src.getRepresentation().stream().map(ElementDefinition40_50::convertPropertyRepresentation).collect(Collectors.toList())); if (src.hasSliceName()) tgt.setSliceNameElement(String40_50.convertString(src.getSliceNameElement())); @@ -604,10 +615,66 @@ public class ElementDefinition40_50 { public static org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent convertElementDefinitionBindingComponent(org.hl7.fhir.r4.model.ElementDefinition.ElementDefinitionBindingComponent src) throws FHIRException { if (src == null) return null; org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent tgt = new org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent(); - ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt); + ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt, + "http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional"); if (src.hasStrength()) tgt.setStrengthElement(Enumerations40_50.convertBindingStrength(src.getStrengthElement())); if (src.hasDescription()) tgt.setDescriptionElement(String40_50.convertStringToMarkdown(src.getDescriptionElement())); if (src.hasValueSet()) tgt.setValueSetElement(Canonical40_50.convertCanonical(src.getValueSetElement())); + + for (org.hl7.fhir.r4.model.Extension ext : src.getExtensionsByUrl("http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional")) { + tgt.addAdditional(convertAdditional(ext)); + } + return tgt; + } + + private static ElementDefinitionBindingAdditionalComponent convertAdditional(Extension src) { + if (src == null) return null; + ElementDefinitionBindingAdditionalComponent tgt = new ElementDefinitionBindingAdditionalComponent(); + ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt, "valueSet", "purpose", "documentation", "shortDoco", "usage", "any"); + if (src.hasExtension("purpose")) { + tgt.getPurposeElement().setValueAsString(src.getExtensionByUrl("purpose").getValue().primitiveValue()); + } + if (src.hasExtension("valueSet")) { + tgt.setValueSetElement(Canonical40_50.convertCanonical((CanonicalType) src.getExtensionByUrl("valueSet").getValue())); + } + if (src.hasExtension("documentation")) { + tgt.setDocumentationElement(MarkDown40_50.convertMarkdown((MarkdownType) src.getExtensionByUrl("documentation").getValue())); + } + if (src.hasExtension("shortDoco")) { + tgt.setShortDocoElement(String40_50.convertString((StringType) src.getExtensionByUrl("shortDoco").getValue())); + } + for (Extension t : src.getExtensionsByUrl("usage")) { + tgt.addUsage(UsageContext40_50.convertUsageContext((org.hl7.fhir.r4.model.UsageContext) t.getValue())); + } + if (src.hasExtension("any")) { + tgt.setAnyElement(Boolean40_50.convertBoolean((BooleanType) src.getExtensionByUrl("any").getValue())); + } + return tgt; + } + + private static Extension convertAdditional(ElementDefinitionBindingAdditionalComponent src) { + if (src == null) return null; + Extension tgt = new Extension(); + ConversionContext40_50.INSTANCE.getVersionConvertor_40_50().copyElement(src, tgt); + if (src.hasPurpose()) { + tgt.addExtension(new Extension("purpose", new CodeType(src.getPurposeElement().primitiveValue()))); + } + if (src.hasValueSet()) { + tgt.addExtension(new Extension("valueSet", Canonical40_50.convertCanonical(src.getValueSetElement()))); + } + if (src.hasDocumentation()) { + tgt.addExtension(new Extension("documentation", MarkDown40_50.convertMarkdown(src.getDocumentationElement()))); + } + if (src.hasShortDoco()) { + tgt.addExtension(new Extension("shortDoco", String40_50.convertString(src.getShortDocoElement()))); + } + for (UsageContext t : src.getUsage()) { + tgt.addExtension(new Extension("usage", UsageContext40_50.convertUsageContext(t))); + } + if (src.hasAny()) { + tgt.addExtension(new Extension("any", Boolean40_50.convertBoolean(src.getAnyElement()))); + } + return tgt; } @@ -618,6 +685,9 @@ public class ElementDefinition40_50 { if (src.hasStrength()) tgt.setStrengthElement(Enumerations40_50.convertBindingStrength(src.getStrengthElement())); if (src.hasDescription()) tgt.setDescriptionElement(String40_50.convertString(src.getDescriptionElement())); if (src.hasValueSet()) tgt.setValueSetElement(Canonical40_50.convertCanonical(src.getValueSetElement())); + for (ElementDefinitionBindingAdditionalComponent ab : src.getAdditional()) { + tgt.addExtension(convertAdditional(ab)); + } return tgt; } diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv43_50/datatypes43_50/special43_50/ElementDefinition43_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv43_50/datatypes43_50/special43_50/ElementDefinition43_50.java index 44e7ddb2b..507366fe4 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv43_50/datatypes43_50/special43_50/ElementDefinition43_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv43_50/datatypes43_50/special43_50/ElementDefinition43_50.java @@ -3,6 +3,12 @@ package org.hl7.fhir.convertors.conv43_50.datatypes43_50.special43_50; import java.util.stream.Collectors; import org.hl7.fhir.convertors.context.ConversionContext43_50; +import org.hl7.fhir.convertors.context.ConversionContext43_50; +import org.hl7.fhir.convertors.conv43_50.datatypes43_50.metadata43_50.UsageContext43_50; +import org.hl7.fhir.convertors.conv43_50.datatypes43_50.primitive43_50.Boolean43_50; +import org.hl7.fhir.convertors.conv43_50.datatypes43_50.primitive43_50.Canonical43_50; +import org.hl7.fhir.convertors.conv43_50.datatypes43_50.primitive43_50.MarkDown43_50; +import org.hl7.fhir.convertors.conv43_50.datatypes43_50.primitive43_50.String43_50; import org.hl7.fhir.convertors.conv43_50.datatypes43_50.BackboneElement43_50; import org.hl7.fhir.convertors.conv43_50.datatypes43_50.general43_50.Coding43_50; import org.hl7.fhir.convertors.conv43_50.datatypes43_50.primitive43_50.Boolean43_50; @@ -16,6 +22,14 @@ import org.hl7.fhir.convertors.conv43_50.datatypes43_50.primitive43_50.UnsignedI import org.hl7.fhir.convertors.conv43_50.datatypes43_50.primitive43_50.Uri43_50; import org.hl7.fhir.convertors.conv43_50.resources43_50.Enumerations43_50; import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.r4b.model.BooleanType; +import org.hl7.fhir.r4b.model.CanonicalType; +import org.hl7.fhir.r4b.model.Extension; +import org.hl7.fhir.r4b.model.MarkdownType; +import org.hl7.fhir.r4b.model.StringType; +import org.hl7.fhir.r5.model.CodeType; +import org.hl7.fhir.r5.model.UsageContext; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; public class ElementDefinition43_50 { public static org.hl7.fhir.r5.model.ElementDefinition convertElementDefinition(org.hl7.fhir.r4b.model.ElementDefinition src) throws FHIRException { @@ -605,10 +619,14 @@ public class ElementDefinition43_50 { public static org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent convertElementDefinitionBindingComponent(org.hl7.fhir.r4b.model.ElementDefinition.ElementDefinitionBindingComponent src) throws FHIRException { if (src == null) return null; org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent tgt = new org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent(); - ConversionContext43_50.INSTANCE.getVersionConvertor_43_50().copyElement(src, tgt); + ConversionContext43_50.INSTANCE.getVersionConvertor_43_50().copyElement(src, tgt, + "http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional"); if (src.hasStrength()) tgt.setStrengthElement(Enumerations43_50.convertBindingStrength(src.getStrengthElement())); if (src.hasDescription()) tgt.setDescriptionElement(String43_50.convertStringToMarkdown(src.getDescriptionElement())); if (src.hasValueSet()) tgt.setValueSetElement(Canonical43_50.convertCanonical(src.getValueSetElement())); + for (org.hl7.fhir.r4b.model.Extension ext : src.getExtensionsByUrl("http://hl7.org/fhir/5.0/StructureDefinition/extension-ElementDefinition.binding.additional")) { + tgt.addAdditional(convertAdditional(ext)); + } return tgt; } @@ -619,9 +637,64 @@ public class ElementDefinition43_50 { if (src.hasStrength()) tgt.setStrengthElement(Enumerations43_50.convertBindingStrength(src.getStrengthElement())); if (src.hasDescription()) tgt.setDescriptionElement(String43_50.convertString(src.getDescriptionElement())); if (src.hasValueSet()) tgt.setValueSetElement(Canonical43_50.convertCanonical(src.getValueSetElement())); + for (ElementDefinitionBindingAdditionalComponent ab : src.getAdditional()) { + tgt.addExtension(convertAdditional(ab)); + } return tgt; } + + private static ElementDefinitionBindingAdditionalComponent convertAdditional(Extension src) { + if (src == null) return null; + ElementDefinitionBindingAdditionalComponent tgt = new ElementDefinitionBindingAdditionalComponent(); + ConversionContext43_50.INSTANCE.getVersionConvertor_43_50().copyElement(src, tgt, "valueSet", "purpose", "documentation", "shortDoco", "usage", "any"); + if (src.hasExtension("purpose")) { + tgt.getPurposeElement().setValueAsString(src.getExtensionByUrl("purpose").getValue().primitiveValue()); + } + if (src.hasExtension("valueSet")) { + tgt.setValueSetElement(Canonical43_50.convertCanonical((CanonicalType) src.getExtensionByUrl("valueSet").getValue())); + } + if (src.hasExtension("documentation")) { + tgt.setDocumentationElement(MarkDown43_50.convertMarkdown((MarkdownType) src.getExtensionByUrl("documentation").getValue())); + } + if (src.hasExtension("shortDoco")) { + tgt.setShortDocoElement(String43_50.convertString((StringType) src.getExtensionByUrl("shortDoco").getValue())); + } + for (Extension t : src.getExtensionsByUrl("usage")) { + tgt.addUsage(UsageContext43_50.convertUsageContext((org.hl7.fhir.r4b.model.UsageContext) t.getValue())); + } + if (src.hasExtension("any")) { + tgt.setAnyElement(Boolean43_50.convertBoolean((BooleanType) src.getExtensionByUrl("any").getValue())); + } + return tgt; + } + + private static Extension convertAdditional(ElementDefinitionBindingAdditionalComponent src) { + if (src == null) return null; + Extension tgt = new Extension(); + ConversionContext43_50.INSTANCE.getVersionConvertor_43_50().copyElement(src, tgt); + if (src.hasPurpose()) { + tgt.addExtension(new Extension("purpose", new CodeType(src.getPurposeElement().primitiveValue()))); + } + if (src.hasValueSet()) { + tgt.addExtension(new Extension("valueSet", Canonical43_50.convertCanonical(src.getValueSetElement()))); + } + if (src.hasDocumentation()) { + tgt.addExtension(new Extension("documentation", MarkDown43_50.convertMarkdown(src.getDocumentationElement()))); + } + if (src.hasShortDoco()) { + tgt.addExtension(new Extension("shortDoco", String43_50.convertString(src.getShortDocoElement()))); + } + for (UsageContext t : src.getUsage()) { + tgt.addExtension(new Extension("usage", UsageContext43_50.convertUsageContext(t))); + } + if (src.hasAny()) { + tgt.addExtension(new Extension("any", Boolean43_50.convertBoolean(src.getAnyElement()))); + } + + return tgt; + } + public static org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent convertElementDefinitionMappingComponent(org.hl7.fhir.r4b.model.ElementDefinition.ElementDefinitionMappingComponent src) throws FHIRException { if (src == null) return null; org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent tgt = new org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent(); From bd24ebace7b4b2ac6515f70a3c4c956047fe7a98 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 18 May 2024 03:22:46 -0500 Subject: [PATCH 05/14] fix name of preferred when rendering AdditionalBindings --- .../org/hl7/fhir/r5/renderers/AdditionalBindingsRenderer.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/AdditionalBindingsRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/AdditionalBindingsRenderer.java index a2ac7a1c5..f906be94f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/AdditionalBindingsRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/AdditionalBindingsRenderer.java @@ -360,7 +360,7 @@ public class AdditionalBindingsRenderer { if (r5) { td.ah(r5 ? corePath+"valueset-additional-binding-purpose.html#additional-binding-purpose-preferred" : corePath+"terminologies.html#strength", context.formatMessage(RenderingContext.ADD_BIND_RECOM_VALUE_SET)).tx(context.formatMessage(RenderingContext.ADD_BIND_PREF_BIND)); } else { - td.span(null, context.formatMessage(RenderingContext.ADD_BIND_RECOM_VALUE_SET)).tx(context.formatMessage(RenderingContext.ADD_BIND_RECOMMENDED)); + td.span(null, context.formatMessage(RenderingContext.ADD_BIND_RECOM_VALUE_SET)).tx(context.formatMessage(RenderingContext.ADD_BIND_PREFERRED)); } break; case "ui" : From 6ba9a11c516c606c6c123a587dc64bfdc4fe9201 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 18 May 2024 03:24:54 -0500 Subject: [PATCH 06/14] Validate Additional Bindings (provisional - usage context is todo) --- .../org/hl7/fhir/r5/model/Parameters.java | 10 + .../hl7/fhir/r5/utils/ToolingExtensions.java | 8 + .../fhir/utilities/i18n/I18nConstants.java | 2 + .../utilities/i18n/RenderingI18nContext.java | 2 +- .../src/main/resources/Messages.properties | 5 +- .../resources/rendering-phrases.properties | 2 +- .../resources/rendering-phrases_fr.properties | 149 ---- .../hl7/fhir/validation/BaseValidator.java | 38 + .../instance/InstanceValidator.java | 787 +++++++++--------- 9 files changed, 467 insertions(+), 536 deletions(-) delete mode 100644 org.hl7.fhir.utilities/src/main/resources/rendering-phrases_fr.properties diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java index 778594762..8ad954266 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/model/Parameters.java @@ -1527,6 +1527,16 @@ public String toString() { return null; } + + public boolean hasPart(String name) { + for (ParametersParameterComponent t : getPart()) { + if (name.equals(t.getName())) { + return true; + } + } + return false; + } + // end addition } 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 210f42dc2..0231aee97 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 @@ -428,6 +428,14 @@ public class ToolingExtensions { } return null; } + + public static String readStringFromExtension(Extension ext) { + if (ext.hasValue() && ext.getValue().isPrimitive()) { + return ext.getValue().primitiveValue(); + } + return null; + } + public static String readStringExtension(Element c, String uri) { Extension ex = ExtensionHelper.getExtension(c, uri); if (ex == null) 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 9165aaeda..160a53bdd 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 @@ -1105,6 +1105,8 @@ public class I18nConstants { public static final String SD_ELEMENT_REASON_DERIVED = "SD_ELEMENT_REASON_DERIVED"; public static final String SD_ELEMENT_PATTERN_WRONG_TYPE = "SD_ELEMENT_PATTERN_WRONG_TYPE"; public static final String FHIRPATH_REDEFINE_VARIABLE = "FHIRPATH_REDEFINE_VARIABLE"; + public static final String BINDING_ADDITIONAL = "BINDING_ADDITIONAL"; + public static final String BINDING_MAX = "BINDING_MAX"; } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java index 23fd4a468..01a2d927e 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java @@ -864,7 +864,7 @@ public class RenderingI18nContext extends I18nBase { public static final String ADD_BIND_VALID_EXT = "ADD_BIND_VALID_EXT"; public static final String ADD_BIND_NEW_REC = "ADD_BIND_NEW_REC"; public static final String ADD_BIND_RECOM_VALUE_SET = "ADD_BIND_RECOM_VALUE_SET"; - public static final String ADD_BIND_RECOMMENDED = "ADD_BIND_RECOMMENDED"; + public static final String ADD_BIND_PREFERRED = "ADD_BIND_PREFERRED"; public static final String ADD_BIND_REQUIRED = "ADD_BIND_REQUIRED"; public static final String ADD_BIND_GIVEN_CONT = "ADD_BIND_GIVEN_CONT"; public static final String ADD_BIND_UI_BIND = "ADD_BIND_UI_BIND"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index d0eb7a831..6c76d8fe1 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -1120,7 +1120,7 @@ CODESYSTEM_PROPERTY_UNKNOWN_CODE = This property has only a code (''{0}'') and n CODESYSTEM_PROPERTY_KNOWN_CODE_SUGGESTIVE = This property has only the standard code (''{0}'') but not the standard URI ''{1}'', so it has no clearly defined meaning in the terminology ecosystem CODESYSTEM_PROPERTY_CODE_TYPE_MISMATCH = Wrong type ''{2}'': The code ''{0}'' identifies a property that has the type ''{1}'' CODESYSTEM_PROPERTY_UNDEFINED = The property ''{0}'' has no definition in CodeSystem.property. Many terminology tools won''t know what to do with it -CODESYSTEM_PROPERTY_NO_VALUE = The property ''{0}'' has no value, and cannot be understoof +CODESYSTEM_PROPERTY_NO_VALUE = The property ''{0}'' has no value, and cannot be understood CODESYSTEM_PROPERTY_WRONG_TYPE = The property ''{0}'' has the invalid type ''{1}'', when it is defined to have the type ''{2}'' CODESYSTEM_DESIGNATION_DISP_CLASH_NO_LANG = The designation ''{0}'' has no use and no language, so is not differentiated from the base display (''{1}'') CODESYSTEM_DESIGNATION_DISP_CLASH_LANG = The designation ''{0}'' has no use and is in the same language (''{2}''), so is not differentiated from the base display (''{1}'') @@ -1143,4 +1143,5 @@ CODESYSTEM_PROPERTY_CODE_WARNING = If the type is ''code'', then the valueSet pr SD_ELEMENT_FIXED_WRONG_TYPE = The base element has a fixed type of ''{0}'', so this element must have a fixed value of the same type, not ''{1}'' SD_ELEMENT_REASON_DERIVED = , because the value must match the fixed value define in ''{0}'' SD_ELEMENT_PATTERN_WRONG_TYPE = The base element has a pattern type of ''{0}'', so this element must have a pattern value of the same type, not ''{1}'' - +BINDING_ADDITIONAL = {0} specified in an additional binding +BINDING_MAX = {0} specified in the max binding diff --git a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties index 760d8f394..8c2808158 100644 --- a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties +++ b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties @@ -870,7 +870,7 @@ ADD_BIND_VALID_REQ = Validators will check this binding (strength = required) ADD_BIND_VALID_EXT = Validators will check this binding (strength = extensible) ADD_BIND_NEW_REC = New records are required to use this value set, but legacy records may use other codes ADD_BIND_RECOM_VALUE_SET = This is the value set that is recommended (documentation should explain why) -ADD_BIND_RECOMMENDED = Recommended +ADD_BIND_PREFERRED = Preferred ADD_BIND_REQUIRED = Required ADD_BIND_GIVEN_CONT = This value set is provided to user look up in a given context ADD_BIND_UI_BIND = UI Binding diff --git a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases_fr.properties b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases_fr.properties deleted file mode 100644 index 8ef8c925f..000000000 --- a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases_fr.properties +++ /dev/null @@ -1,149 +0,0 @@ -# Rendering -BUNDLE_HEADER_ROOT = XXXX -BUNDLE_HEADER_ENTRY = XXXX -BUNDLE_HEADER_ENTRY_URL = XXXX -BUNDLE_RESOURCE = XXXX -BUNDLE_SEARCH = XXXX -BUNDLE_SEARCH_MODE = XXXX -BUNDLE_SEARCH_SCORE = XXXX -BUNDLE_RESPONSE = XXXX -BUNDLE_LOCATION = XXXX -BUNDLE_ETAG = XXXX -BUNDLE_LAST_MOD = XXXX -BUNDLE_REQUEST = XXXX -BUNDLE_IF_NON_MATCH = XXXX -BUNDLE_IF_MOD = XXXX -BUNDLE_IF_MATCH = XXXX -BUNDLE_IF_NONE = XXXX -CODEPROP_CODE = XXXX -CODESYSTEM_PROPS = XXXX -CODESYSTEM_CONCEPTS = XXXX -CODESYSTEM_DEPRECATED = XXXX -CODESYSTEM_DESC = XXXX -CODESYSTEM_FILTERS = XXXX -CODESYSTEM_FILTER_CODE = XXXX -CODESYSTEM_FILTER_OP = XXXX -CODESYSTEM_FILTER_VALUE = XXXX -CODESYSTEM_PROP_CODE = XXXX -CODESYSTEM_PROP_NAME = XXXX -CODESYSTEM_PROP_URI = XXXX -CODESYSTEM_PROP_DESC = XXXX -CODESYSTEM_PROP_TYPE = XXXX -CODESYSTEM_PROPS_DESC = XXXX -CODESYSTEM_CONTENT_COMPLETE ThXXXX -CODESYSTEM_CONTENT_EXAMPLE = XXXX -CODESYSTEM_CONTENT_FRAGMENT = XXXX -CODESYSTEM_CONTENT_NOTPRESENT = XXXX -CODESYSTEM_CONTENT_SUPPLEMENT = XXXX -RESOURCE_COPYRIGHT = XXXX -SD_COMP_HEAD_CARD_L = XXXX -SD_COMP_HEAD_CARD_L_DESC =XXXX -SD_COMP_HEAD_CARD_R = XXXX -SD_COMP_HEAD_CARD_R_DESC =XXXX -SD_COMP_HEAD_COMP = XXXX -SD_COMP_HEAD_COMP_DESC = XXXX -SD_COMP_HEAD_DESC_L = XXXX -SD_COMP_HEAD_DESC_L_DESC =XXXX -SD_COMP_HEAD_DESC_R = XXXX -SD_COMP_HEAD_DESC_R_DESC =XXXX -SD_COMP_HEAD_FLAGS_L = XXXX -SD_COMP_HEAD_FLAGS_L_DESC =XXXX -SD_COMP_HEAD_FLAGS_R = XXXX -SD_COMP_HEAD_FLAGS_R_DESC =XXXX -SD_COMP_HEAD_NAME = XXXX -SD_COMP_HEAD_NAME_DESC = XXXX -SD_COMP_HEAD_TYPE_L = XXXX -SD_COMP_HEAD_TYPE_L_DESC =XXXX -SD_COMP_HEAD_TYPE_R = XXXX -SD_COMP_HEAD_TYPE_R_DESC =XXXX -SD_DOCO = XXXX -SD_GRID_HEAD_CARD = XXXX -SD_GRID_HEAD_CARD_DESC = XXXX -SD_GRID_HEAD_DESC = XXXX -SD_GRID_HEAD_DESC_DESC = XXXX -SD_GRID_HEAD_NAME = XXXX -SD_GRID_HEAD_NAME_DESC = XXXX -SD_GRID_HEAD_TYPE = XXXX -SD_GRID_HEAD_TYPE_DESC = XXXX -SD_HEAD_CARD = XXXX -SD_HEAD_CARD_DESC = XXXX -SD_HEAD_DESC = XXXX -SD_HEAD_DESC_DESC = XXXX -SD_HEAD_FLAGS_DESC = XXXX -SD_HEAD_NAME = XXXX -SD_HEAD_SC = XXXX -SD_HEAD_TYPE = XXXX -SD_HEAD_TYPE_DESC = XXXX -SD_LEGEND = XXXX -SD_SUMMARY = XXXX -SD_SUMMARY_MAPPINGS = XXXX -SD_SUMMARY_MISSING_EXTENSION = XXXX -SD_SUMMARY_MISSING_PROFILE = XXXX -SD_SUMMARY_PUBLICATION = XXXX -SD_SUMMARY_SLICES = XXXX -SD_SUMMARY_SLICE_NONE = XXXX -SD_SUMMARY_SLICE_one = XXXX -SD_SUMMARY_SLICE_other = XXXX -SD_SUMMARY_MANDATORY = XXXX -SD_SUMMARY_MUST_SUPPORT = XXXX -SD_SUMMARY_FIXED = XXXX -SD_SUMMARY_INFO = XXXX -SD_SUMMARY_PROHIBITED = XXXX -SD_SUMMARY_NESTED_MANDATORY = XXXX -TX_CODE = XXXX -TX_COMMENTS = XXXX -TX_DEFINITION = XXXX -TX_DEPRECATED = XXXX -TX_DISPLAY = XXXX -TX_VERSION = XXXX -PAT_NO_GENDER = XXXX -PAT_GENDER = XXXX -PAT_NO_DOB = XXXX -PAT_DOB = XXXX -PAT_NO_NAME = XXXX -PAT_CONTAINED_one = XXXX -PAT_CONTAINED_other = XXXX -PAT_OTHER_ID_one = XXXX -PAT_OTHER_ID_other = XXXX -PAT_OTHER_ID_HINT_one = XXXX -PAT_OTHER_ID_HINT_other = XXXX -PAT_LANG_one = XXXX -PAT_LANG_other = XXXX -PAT_LANG_HINT_one = XXXX -PAT_LANG_HINT_other = XXXX -PAT_LANG_PREFERRED = XXXX -PAT_GP = XXXX -PAT_MO = XXXX -PAT_LINKS = XXXX -PAT_LINKS_HINT = XXXX -PAT_LINK_REPLBY = XXXX -PAT_LINK_REPL = XXXX -PAT_LINK_REFER = XXXX -PAT_LINK_SEE = XXXX -PAT_NOM_CONTACT = XXXX -PAT_NOK_CONTACT = XXXX -PAT_NOK_CONTACT_HINT = XXXX -PAT_RELN = XXXX -PAT_ORG = XXXX -PAT_PERIOD = XXXX -PAT_ALT_NAME = XXXX -PAT_ALT_NAME_HINT = XXXX -PAT_CONTACT = XXXX -PAT_CONTACT_HINT = XXXX -PAT_ACTIVE = XXXX -PAT_ACTIVE_HINT = XXXX -PAT_DECEASED = XXXX -PAT_DECEASED_HINT = XXXX -PAT_MARITAL = XXXX -PAT_MARITAL_HINT = XXXX -PAT_MUL_BIRTH = XXXX -PAT_MUL_BIRTH_HINT = XXXX -PAT_PHOTO = XXXX -SD_HEAD_NAME_DESC = XXXX -SD_HEAD_FLAGS = XXXX -SD_SLICING_INFO = XXX -BUNDLE_DOCUMENT_CONTENT = XXXX -BUNDLE_HEADER_DOC_ENTRY_URD = XXXX -BUNDLE_HEADER_DOC_ENTRY_U = XXXX -BUNDLE_HEADER_DOC_ENTRY_RD = XXXX - \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java index 9e88c6b4c..1701bc162 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/BaseValidator.java @@ -109,6 +109,9 @@ public class BaseValidator implements IValidationContextResourceLoader { public void see(boolean ok) { value = value && ok; } + public void set(boolean value) { + this.value = value; + } } @@ -1614,4 +1617,39 @@ public class BaseValidator implements IValidationContextResourceLoader { } + protected boolean isKnownUsage(UsageContext usage) { + for (UsageContext t : usageContexts) { + if (usagesMatch(usage, t)) { + return true; + } + } + return false; + } + + private boolean usagesMatch(UsageContext usage, UsageContext t) { + if (usage.hasCode() && t.hasCode() && usage.hasValue() && t.hasValue()) { + if (usage.getCode().matches(t.getCode())) { + if (usage.getValue().fhirType().equals(t.getValue().fhirType())) { + switch (usage.getValue().fhirType()) { + case "CodeableConcept": + for (Coding uc : usage.getValueCodeableConcept().getCoding()) { + for (Coding tc : t.getValueCodeableConcept().getCoding()) { + if (uc.matches(tc)) { + return true; + } + } + } + case "Quantity": + return false; // for now + case "Range": + return false; // for now + case "Reference": + return false; // for now + } + } + } + } + return false; + } + } \ 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 b690d2c31..365f4f039 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 @@ -114,9 +114,11 @@ import org.hl7.fhir.r5.model.DateTimeType; import org.hl7.fhir.r5.model.DateType; import org.hl7.fhir.r5.model.DecimalType; import org.hl7.fhir.r5.model.ElementDefinition; +import org.hl7.fhir.r5.model.ElementDefinition.AdditionalBindingPurposeVS; import org.hl7.fhir.r5.model.ElementDefinition.AggregationMode; import org.hl7.fhir.r5.model.ElementDefinition.ConstraintSeverity; import org.hl7.fhir.r5.model.ElementDefinition.DiscriminatorType; +import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionMappingComponent; @@ -212,6 +214,7 @@ import org.hl7.fhir.validation.BaseValidator; import org.hl7.fhir.validation.cli.model.HtmlInMarkdownCheck; import org.hl7.fhir.validation.cli.utils.QuestionnaireMode; import org.hl7.fhir.validation.codesystem.CodingsObserver; +import org.hl7.fhir.validation.instance.InstanceValidator.BindingContext; import org.hl7.fhir.validation.instance.type.BundleValidator; import org.hl7.fhir.validation.instance.type.CodeSystemValidator; import org.hl7.fhir.validation.instance.type.ConceptMapValidator; @@ -255,6 +258,11 @@ import org.w3c.dom.Document; public class InstanceValidator extends BaseValidator implements IResourceValidator { + public enum BindingContext { + BASE, MAXVS, ADDITIONAL + + } + private static final String EXECUTED_CONSTRAINT_LIST = "validator.executed.invariant.list"; private static final String EXECUTION_ID = "validator.execution.id"; private static final String HTML_FRAGMENT_REGEX = "[a-zA-Z]\\w*(((\\s+)(\\S)*)*)"; @@ -1346,129 +1354,40 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private boolean checkCodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack, BooleanHolder bh) { boolean checkDisp = true; - boolean checked = false; + BooleanHolder checked = new BooleanHolder(false); if (!noTerminologyChecks && theElementCntext != null && theElementCntext.hasBinding()) { ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); if (warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING, path)) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl(), profile); - if (valueset == null) { - CodeSystem cs = context.fetchCodeSystem(binding.getValueSet()); - if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(binding.getValueSet()))) { - warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet())); - } else { - bh.fail(); - } - } else { - try { - CodeableConcept cc = ObjectConverter.readAsCodeableConcept(element); - if (!cc.hasCoding()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET, describeReference(binding.getValueSet(), valueset))); - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) - bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET)), valueset.getVersionedUrl())); - else - warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(binding.getValueSet(), valueset)); - } - } else { - long t = System.nanoTime(); - - // Check whether the codes are appropriate for the type of binding we have - boolean bindingsOk = true; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - if (binding.getStrength() == BindingStrength.REQUIRED) { - removeTrackedMessagesForLocation(errors, element, path); - } - boolean atLeastOneSystemIsSupported = false; - for (Coding nextCoding : cc.getCoding()) { - String nextSystem = nextCoding.getSystem(); - if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem, baseOptions.getFhirVersion())) { - atLeastOneSystemIsSupported = true; - break; - } - } - - if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { - // ignore this since we can't validate but it doesn't matter.. - } else { - checked = true; - ValidationResult vr = checkCodeOnServer(stack, valueset, cc); - bh.see(processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), false, binding.getValueSet())); - if (!vr.isOk()) { - bindingsOk = false; - if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) { - if (binding.getStrength() == BindingStrength.REQUIRED || (binding.getStrength() == BindingStrength.EXTENSIBLE && binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET))) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOSVC_BOUND_REQ, describeReference(binding.getValueSet())); - } else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOSVC_BOUND_EXT, describeReference(binding.getValueSet())); - } - } else if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) - bh.see(checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), cc, stack)); - else if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_2_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_3_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); - } - } - } else { - if (binding.getStrength() == BindingStrength.REQUIRED) { - bh.see(txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(binding.getValueSet(), valueset), ccSummary(cc))); - } else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) - bh.see(checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), cc, stack)); - if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(binding.getValueSet(), valueset), ccSummary(cc)); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(binding.getValueSet(), valueset), ccSummary(cc)); - } - } - } - } else if (vr.getMessage() != null) { - if (vr.getTrimmedMessage() != null) { - if (vr.getSeverity() == IssueSeverity.INFORMATION) { - txHint(errors, "2023-07-03", vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else { - checkDisp = false; - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getTrimmedMessage()); - } - } - } else { - if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - removeTrackedMessagesForLocation(errors, element, path); - } - checkDisp = false; - } - } - // Then, for any codes that are in code systems we are able - // to validate, we'll validate that the codes actually exist - if (bindingsOk) { - for (Coding nextCoding : cc.getCoding()) { - bh.see(checkBindings(errors, path, element, stack, valueset, nextCoding)); - } - } - timeTracker.tx(t, "vc "+cc.toString()); - } - } - } catch (CheckCodeOnServerException e) { - if (STACK_TRACE) e.getCause().printStackTrace(); - warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODEABLECONCEPT, e.getCause().getMessage()); + try { + CodeableConcept cc = ObjectConverter.readAsCodeableConcept(element); + if (binding.hasValueSet()) { + String vsRef = binding.getValueSet(); + ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); + BindingStrength strength = binding.getStrength(); + Extension maxVS = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET); + + checkDisp = validateBindingCodeableConcept(errors, path, element, profile, stack, bh, checkDisp, checked, cc, vsRef, valueset, strength, maxVS, true); +// } else if (binding.hasValueSet()) { +// hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); + } else if (!noBindingMsgSuppressed) { + hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); + } + for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) { + if (isTestableBinding(ab) && isInScope(ab)) { + String vsRef = ab.getValueSet(); + ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); + BindingStrength strength = convertPurposeToStrength(ab.getPurpose()); + + checkDisp = validateBindingCodeableConcept(errors, path, element, profile, stack, bh, checkDisp, checked, cc, vsRef, valueset, strength, null, false) && checkDisp; } } - } else if (binding.hasValueSet()) { - hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); - } else if (!noBindingMsgSuppressed) { - hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); + } catch (CheckCodeOnServerException e) { + if (STACK_TRACE) e.getCause().printStackTrace(); + warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODEABLECONCEPT, e.getCause().getMessage()); } } } - if (!noTerminologyChecks && theElementCntext != null && !checked) { // no binding check, so we just check the CodeableConcept generally + if (!noTerminologyChecks && theElementCntext != null && !checked.ok()) { // no binding check, so we just check the CodeableConcept generally try { CodeableConcept cc = ObjectConverter.readAsCodeableConcept(element); if (cc.hasCoding()) { @@ -1485,6 +1404,144 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return checkDisp; } + private boolean isInScope(ElementDefinitionBindingAdditionalComponent ab) { + for (UsageContext usage : ab.getUsage()) { + if (isInScope(usage)) { + return true; + } + } + return ab.getUsage().isEmpty(); + } + + private boolean isInScope(UsageContext usage) { + if (isKnownUsage(usage)) { + return true; + } + return false; + } + + private BindingStrength convertPurposeToStrength(AdditionalBindingPurposeVS purpose) { + switch (purpose) { + case MAXIMUM: return BindingStrength.REQUIRED; + case PREFERRED: return BindingStrength.PREFERRED; + case REQUIRED: return BindingStrength.REQUIRED; + default: return null; + } + } + + private boolean isTestableBinding(ElementDefinitionBindingAdditionalComponent ab) { + return ab.hasValueSet() && convertPurposeToStrength(ab.getPurpose()) != null; + } + + private boolean validateBindingCodeableConcept(List errors, String path, Element element, StructureDefinition profile, NodeStack stack, BooleanHolder bh, boolean checkDisp, BooleanHolder checked, + CodeableConcept cc, String vsRef, ValueSet valueset, BindingStrength strength, Extension maxVS, boolean base) throws CheckCodeOnServerException { + if (valueset == null) { + CodeSystem cs = context.fetchCodeSystem(vsRef); + if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) { + warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(vsRef)); + } else { + bh.fail(); + } + } else { + BindingContext bc = base ? BindingContext.BASE : BindingContext.ADDITIONAL; + if (!cc.hasCoding()) { + if (strength == BindingStrength.REQUIRED) + bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET, describeReference(vsRef, valueset, bc))); + else if (strength == BindingStrength.EXTENSIBLE) { + if (maxVS != null) + bh.see(rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringFromExtension(maxVS)), valueset.getVersionedUrl())); + else + warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(vsRef, valueset, bc)); + } + } else { + long t = System.nanoTime(); + + // Check whether the codes are appropriate for the type of binding we have + boolean bindingsOk = true; + if (strength != BindingStrength.EXAMPLE) { + if (strength == BindingStrength.REQUIRED) { + removeTrackedMessagesForLocation(errors, element, path); + } + boolean atLeastOneSystemIsSupported = false; + for (Coding nextCoding : cc.getCoding()) { + String nextSystem = nextCoding.getSystem(); + if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem, baseOptions.getFhirVersion())) { + atLeastOneSystemIsSupported = true; + break; + } + } + + if (!atLeastOneSystemIsSupported && strength == BindingStrength.EXAMPLE) { + // ignore this since we can't validate but it doesn't matter.. + } else { + checked.set(true); + ValidationResult vr = checkCodeOnServer(stack, valueset, cc); + bh.see(processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(strength), false, vsRef)); + if (!vr.isOk()) { + bindingsOk = false; + if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.NOSERVICE) { + if (strength == BindingStrength.REQUIRED || (strength == BindingStrength.EXTENSIBLE && maxVS != null)) { + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOSVC_BOUND_REQ, describeReference(vsRef)); + } else if (strength == BindingStrength.EXTENSIBLE) { + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOSVC_BOUND_EXT, describeReference(vsRef)); + } + } else if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { + if (strength == BindingStrength.REQUIRED) + txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1_CC, describeReference(vsRef), vr.getErrorClass().toString()); + else if (strength == BindingStrength.EXTENSIBLE) { + if (maxVS != null) + bh.see(checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(maxVS), cc, stack)); + else if (!noExtensibleWarnings) + txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_2_CC, describeReference(vsRef), vr.getErrorClass().toString()); + } else if (strength == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_3_CC, describeReference(vsRef), vr.getErrorClass().toString()); + } + } + } else { + if (strength == BindingStrength.REQUIRED) { + bh.see(txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(vsRef, valueset, bc), ccSummary(cc))); + } else if (strength == BindingStrength.EXTENSIBLE) { + if (maxVS != null) + bh.see(checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(maxVS), cc, stack)); + if (!noExtensibleWarnings) + txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(vsRef, valueset, bc), ccSummary(cc)); + } else if (strength == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(vsRef, valueset, bc), ccSummary(cc)); + } + } + } + } else if (vr.getMessage() != null) { + if (vr.getTrimmedMessage() != null) { + if (vr.getSeverity() == IssueSeverity.INFORMATION) { + txHint(errors, "2023-07-03", vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else { + checkDisp = false; + txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getTrimmedMessage()); + } + } + } else { + if (strength == BindingStrength.EXTENSIBLE) { + removeTrackedMessagesForLocation(errors, element, path); + } + checkDisp = false; + } + } + // Then, for any codes that are in code systems we are able + // to validate, we'll validate that the codes actually exist + if (bindingsOk) { + for (Coding nextCoding : cc.getCoding()) { + bh.see(checkBindings(errors, path, element, stack, valueset, nextCoding)); + } + } + timeTracker.tx(t, "vc "+cc.toString()); + } + } + } + return checkDisp; + } + /** * The terminology server will report an error for an unknown code system or version, or a dependent valueset * @@ -1492,10 +1549,10 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat * @param binding * @return */ - private org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity notFoundSeverityForBinding(ElementDefinitionBindingComponent binding) { - if (binding.getStrength() == BindingStrength.REQUIRED) { + private org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity notFoundSeverityForBinding(BindingStrength strength) { + if (strength == BindingStrength.REQUIRED) { return org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.ERROR; - } else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { + } else if (strength == BindingStrength.EXTENSIBLE) { return org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.WARNING; } else { return org.hl7.fhir.r5.model.OperationOutcome.IssueSeverity.INFORMATION; @@ -1550,131 +1607,45 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private boolean checkCDACodeableConcept(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, NodeStack stack, StructureDefinition logical) { - boolean ok = true; + BooleanHolder ok = new BooleanHolder(true); if (!noTerminologyChecks && theElementCntext != null && theElementCntext.hasBinding()) { - ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); - if (warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING, path)) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl(), profile); - if (valueset == null) { - CodeSystem cs = context.fetchCodeSystem(binding.getValueSet()); - if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(binding.getValueSet()))) { - warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet())); - } else { - ok = false; - } - } else { - try { - CodeableConcept cc = new CodeableConcept(); - ok = convertCDACodeToCodeableConcept(errors, path, element, logical, cc) && ok; - if (!cc.hasCoding()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - ok = rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, "No code provided, and a code is required from the value set " + describeReference(binding.getValueSet()) + " (" + valueset.getVersionedUrl()) && ok; - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) - ok = rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESETMAX, describeReference(ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET)), valueset.getVersionedUrl()) && ok; - else - warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_VALUESET_EXT, describeReference(binding.getValueSet(), valueset)); - } - } else { - long t = System.nanoTime(); + try { + CodeableConcept cc = new CodeableConcept(); + ok.see(convertCDACodeToCodeableConcept(errors, path, element, logical, cc)); + ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); + if (warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING, path)) { + if (binding.hasValueSet()) { + String vsRef = binding.getValueSet(); + ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); + BindingStrength strength = binding.getStrength(); + Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET); + + validateBindingCodeableConcept(errors, path, element, profile, stack, ok, false, new BooleanHolder(), cc, vsRef, valueset, strength, vsMax, true); - // Check whether the codes are appropriate for the type of binding we have - boolean bindingsOk = true; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - if (binding.getStrength() == BindingStrength.REQUIRED) { - removeTrackedMessagesForLocation(errors, element, path); - } - - boolean atLeastOneSystemIsSupported = false; - for (Coding nextCoding : cc.getCoding()) { - String nextSystem = nextCoding.getSystem(); - if (isNotBlank(nextSystem) && context.supportsSystem(nextSystem, baseOptions.getFhirVersion())) { - atLeastOneSystemIsSupported = true; - break; - } - } - - if (!atLeastOneSystemIsSupported && binding.getStrength() == BindingStrength.EXAMPLE) { - // ignore this since we can't validate but it doesn't matter.. - } else { - ValidationResult vr = checkCodeOnServer(stack, valueset, cc); - ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), false, binding.getValueSet()) && ok; - if (!vr.isOk()) { - bindingsOk = false; - if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_1_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) - ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), cc, stack) && ok; - else if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_2_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_3_CC, describeReference(binding.getValueSet()), vr.getErrorClass().toString()); - } - } - } else { - if (binding.getStrength() == BindingStrength.REQUIRED) - ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_1_CC, describeReference(binding.getValueSet()), valueset, ccSummary(cc)) && ok; - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) - ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), cc, stack) && ok; - if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_2_CC, describeReference(binding.getValueSet(), valueset), ccSummary(cc)); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_3_CC, describeReference(binding.getValueSet(), valueset), ccSummary(cc)); - } - } - } - } else if (vr.getMessage() != null) { - if (vr.getSeverity() == IssueSeverity.INFORMATION) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else { - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } - } else { - ok = true; - } - } - // Then, for any codes that are in code systems we are able - // to validate, we'll validate that the codes actually exist - if (bindingsOk) { - for (Coding nextCoding : cc.getCoding()) { - String nextCode = nextCoding.getCode(); - String nextSystem = nextCoding.getSystem(); - String nextVersion = nextCoding.getVersion(); - if (isNotBlank(nextCode) && isNotBlank(nextSystem) && context.supportsSystem(nextSystem, baseOptions.getFhirVersion())) { - ValidationResult vr = checkCodeOnServer(stack, nextCode, nextSystem, nextVersion, null, false); - ok = (processTxIssues(errors, vr, element, path, null, false, null)) && ok; - if (!vr.isOk()) { - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CODE_NOTVALID, nextCode, nextSystem); - } - } - } - } - timeTracker.tx(t, DataRenderer.display(context, cc)); - } - } - } catch (CheckCodeOnServerException e) { - if (STACK_TRACE) e.getCause().printStackTrace(); - warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODEABLECONCEPT, e.getCause().getMessage()); - } // special case: if the logical model has both CodeableConcept and Coding mappings, we'll also check the first coding. if (getMapping("http://hl7.org/fhir/terminology-pattern", logical, logical.getSnapshot().getElementFirstRep()).contains("Coding")) { - ok = checkTerminologyCoding(errors, path, element, profile, theElementCntext, true, true, stack, logical) && ok; + ok.see(checkTerminologyCoding(errors, path, element, profile, theElementCntext, true, true, stack, logical)); + } +// } else if (binding.hasValueSet()) { +// hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); + } else if (!noBindingMsgSuppressed) { + hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); + } + for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) { + if (isTestableBinding(ab) && isInScope(ab)) { + String vsRef = ab.getValueSet(); + ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); + BindingStrength strength = convertPurposeToStrength(ab.getPurpose()); + validateBindingCodeableConcept(errors, path, element, profile, stack, ok, false, new BooleanHolder(), cc, vsRef, valueset, strength, null, false); } } - } else if (binding.hasValueSet()) { - hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); - } else if (!noBindingMsgSuppressed) { - hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); } + } catch (CheckCodeOnServerException e) { + if (STACK_TRACE) e.getCause().printStackTrace(); + warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODEABLECONCEPT, e.getCause().getMessage()); } } - return ok; + return ok.ok(); } private boolean checkTerminologyCoding(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack, StructureDefinition logical) { @@ -1694,71 +1665,25 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); if (warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING2, path)) { if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl(), profile); - if (valueset == null) { - CodeSystem cs = context.fetchCodeSystem(binding.getValueSet()); - if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(binding.getValueSet()))) { - warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet())); - } else { - ok = false; - } - } else { - try { - long t = System.nanoTime(); - ValidationResult vr = null; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - vr = checkCodeOnServer(stack, valueset, c); - } - if (binding.getStrength() == BindingStrength.REQUIRED) { - removeTrackedMessagesForLocation(errors, element, path); - } - ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), false, binding.getValueSet()) && ok; - timeTracker.tx(t, "vc "+system+"#"+code+" '"+display+"'"); - if (vr != null && !vr.isOk()) { - if (vr.IsNoService()) - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER, system+"#"+code); - else if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(binding.getValueSet(), valueset), vr.getMessage(), system+"#"+code); - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) - checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), c, stack); - else if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(binding.getValueSet(), valueset)); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(binding.getValueSet(), valueset)); - } - } - } else if (binding.getStrength() == BindingStrength.REQUIRED) - ok= txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_4, describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code) && ok; - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) - ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), c, stack) && ok; - else - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_5, describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_6, describeReference(binding.getValueSet(), valueset), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code); - } - } - } else if (vr != null && vr.getMessage() != null){ - if (vr.getSeverity() == IssueSeverity.INFORMATION) { - txHint(errors, "2023-07-04", vr.getTxLink(), IssueType.INFORMATIONAL, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_HINT, code, vr.getMessage()); - } else { - txWarning(errors, "2023-07-04", vr.getTxLink(), IssueType.INFORMATIONAL, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_WARNING, code, vr.getMessage()); - } - } - } catch (Exception e) { - if (STACK_TRACE) e.printStackTrace(); - warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODING1, e.getMessage()); - } - } + String vsRef = binding.getValueSet(); + ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); + BindingStrength strength = binding.getStrength(); + Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET); + + ok = validateBindingTerminologyCoding(errors, path, element, profile, stack, ok, c, code, system, display, vsRef, valueset, strength, vsMax, true); } else if (binding.hasValueSet()) { hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); } else if (!inCodeableConcept && !noBindingMsgSuppressed) { hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); } + for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) { + if (isTestableBinding(ab) && isInScope(ab)) { + String vsRef = ab.getValueSet(); + ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); + BindingStrength strength = convertPurposeToStrength(ab.getPurpose()); + ok = validateBindingTerminologyCoding(errors, path, element, profile, stack, ok, c, code, system, display, vsRef, valueset, strength, null, true) && ok; + } + } } } } else { @@ -1773,6 +1698,72 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return ok; } + private boolean validateBindingTerminologyCoding(List errors, String path, Element element, + StructureDefinition profile, NodeStack stack, boolean ok, Coding c, String code, String system, String display, + String vsRef, ValueSet valueset, BindingStrength strength, Extension vsMax, boolean base) { + if (valueset == null) { + CodeSystem cs = context.fetchCodeSystem(vsRef); + if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) { + warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(vsRef)); + } else { + ok = false; + } + } else { + BindingContext bc = base ? BindingContext.BASE : BindingContext.ADDITIONAL; + try { + long t = System.nanoTime(); + ValidationResult vr = null; + if (strength != BindingStrength.EXAMPLE) { + vr = checkCodeOnServer(stack, valueset, c); + } + if (strength == BindingStrength.REQUIRED) { + removeTrackedMessagesForLocation(errors, element, path); + } + ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(strength), false, vsRef) && ok; + timeTracker.tx(t, "vc "+system+"#"+code+" '"+display+"'"); + if (vr != null && !vr.isOk()) { + if (vr.IsNoService()) + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER, system+"#"+code); + else if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) { + if (strength == BindingStrength.REQUIRED) + txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(vsRef, valueset, bc), vr.getMessage(), system+"#"+code); + else if (strength == BindingStrength.EXTENSIBLE) { + if (vsMax != null) + checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack); + else if (!noExtensibleWarnings) + txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(vsRef, valueset, bc)); + } else if (strength == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(vsRef, valueset, bc)); + } + } + } else if (strength == BindingStrength.REQUIRED) + ok= txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_4, describeReference(vsRef, valueset, bc), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code) && ok; + else if (strength == BindingStrength.EXTENSIBLE) { + if (vsMax != null) + ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack) && ok; + else + txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_5, describeReference(vsRef, valueset, bc), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code); + } else if (strength == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_6, describeReference(vsRef, valueset, bc), (vr.getMessage() != null ? " (error message = " + vr.getMessage() + ")" : ""), system+"#"+code); + } + } + } else if (vr != null && vr.getMessage() != null){ + if (vr.getSeverity() == IssueSeverity.INFORMATION) { + txHint(errors, "2023-07-04", vr.getTxLink(), IssueType.INFORMATIONAL, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_HINT, code, vr.getMessage()); + } else { + txWarning(errors, "2023-07-04", vr.getTxLink(), IssueType.INFORMATIONAL, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_WARNING, code, vr.getMessage()); + } + } + } catch (Exception e) { + if (STACK_TRACE) e.printStackTrace(); + warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODING1, e.getMessage()); + } + } + return ok; + } + private boolean convertCDACodeToCodeableConcept(List errors, String path, Element element, StructureDefinition logical, CodeableConcept cc) { boolean ok = true; cc.setText(element.getNamedChildValue("originalText", false)); @@ -1862,9 +1853,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat timeTracker.tx(t, "vc "+cc.toString()); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_7, describeReference(maxVSUrl, valueset), vr.getMessage()); + txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_7, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), vr.getMessage()); else - ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_8, describeReference(maxVSUrl, valueset), ccSummary(cc)) && ok; + ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_8, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), ccSummary(cc)) && ok; } } catch (CheckCodeOnServerException e) { if (STACK_TRACE) e.getCause().printStackTrace(); @@ -1902,9 +1893,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'"); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset), vr.getMessage(), c.getSystem()+"#"+c.getCode()); + txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), vr.getMessage(), c.getSystem()+"#"+c.getCode()); else - ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_10, describeReference(maxVSUrl, valueset), c.getSystem(), c.getCode()) && ok; + ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_10, describeReference(maxVSUrl, valueset, BindingContext.MAXVS), c.getSystem(), c.getCode()) && ok; } } catch (Exception e) { if (STACK_TRACE) e.printStackTrace(); @@ -1932,9 +1923,9 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat timeTracker.tx(t, "vc "+value); if (!vr.isOk()) { if (vr.getErrorClass() != null && vr.getErrorClass().isInfrastructure()) - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset), vr.getMessage(), value); + txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_9, describeReference(maxVSUrl, valueset, BindingContext.BASE), vr.getMessage(), value); else { - ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_11, describeReference(maxVSUrl, valueset), vr.getMessage()) && ok; + ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_11, describeReference(maxVSUrl, valueset, BindingContext.BASE), vr.getMessage()) && ok; } } } catch (Exception e) { @@ -1980,83 +1971,41 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat private boolean checkCodedElement(List errors, String path, Element element, StructureDefinition profile, ElementDefinition theElementCntext, boolean inCodeableConcept, boolean checkDisplay, NodeStack stack, String theCode, String theSystem, String theVersion, String theDisplay) { boolean ok = true; - boolean checked = false; + BooleanHolder checked = new BooleanHolder(false); if (theSystem != null && theCode != null && !noTerminologyChecks) { try { if (theElementCntext != null && theElementCntext.hasBinding()) { ElementDefinitionBindingComponent binding = theElementCntext.getBinding(); if (warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, binding != null, I18nConstants.TERMINOLOGY_TX_BINDING_MISSING2, path)) { - if (binding.hasValueSet()) { - ValueSet valueset = resolveBindingReference(profile, binding.getValueSet(), profile.getUrl(), profile); - if (valueset == null) { - CodeSystem cs = context.fetchCodeSystem(binding.getValueSet()); - if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(binding.getValueSet()))) { - warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(binding.getValueSet())); - } else { - ok = false; - } - } else { - try { - Coding c = ObjectConverter.readAsCoding(element); - long t = System.nanoTime(); - ValidationResult vr = null; - if (binding.getStrength() != BindingStrength.EXAMPLE) { - checked = true; - vr = checkCodeOnServer(stack, valueset, c); - } - ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), binding.getStrength() == BindingStrength.EXTENSIBLE, binding.getValueSet()) && ok; + try { + Coding c = ObjectConverter.readAsCoding(element); + if (binding.hasValueSet()) { + String vsRef = binding.getValueSet(); + ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); + BindingStrength strength = binding.getStrength(); + Extension vsMax = binding.getExtensionByUrl(ToolingExtensions.EXT_MAX_VALUESET); + + ok = validateBindingCodedElement(errors, path, element, profile, stack, theCode, theSystem, ok, checked, c, vsRef, valueset, strength, vsMax, true); +// } else if (binding.hasValueSet()) { +// hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); + + } else if (!inCodeableConcept && !noBindingMsgSuppressed) { + hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); + } - timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'"); - if (binding.getStrength() == BindingStrength.REQUIRED) { - removeTrackedMessagesForLocation(errors, element, path); - } - - if (vr != null && !vr.isOk()) { - if (vr.IsNoService()) - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER, theSystem+"#"+theCode); - else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { - if (binding.getStrength() == BindingStrength.REQUIRED) - ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(binding.getValueSet(), valueset), vr.getMessage(), theSystem+"#"+theCode) && ok; - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) - checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), c, stack); - else if (!noExtensibleWarnings) - txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(binding.getValueSet(), valueset), theSystem+"#"+theCode); - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(binding.getValueSet(), valueset), theSystem+"#"+theCode); - } - } - } else if (binding.getStrength() == BindingStrength.REQUIRED) - ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_12, describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode) && ok; - else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { - if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) - ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), c, stack) && ok; - else if (!noExtensibleWarnings) { - txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_13, describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage()), c.getSystem()+"#"+c.getCode()); - } - } else if (binding.getStrength() == BindingStrength.PREFERRED) { - if (baseOnly) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_14, describeReference(binding.getValueSet(), valueset), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode); - } - } - } else if (vr != null && vr.getMessage() != null) { - if (vr.getSeverity() == IssueSeverity.INFORMATION) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } else { - txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); - } - } - } catch (Exception e) { - if (STACK_TRACE) e.printStackTrace(); - warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODING1, e.getMessage()); + for (ElementDefinitionBindingAdditionalComponent ab : binding.getAdditional()) { + if (isTestableBinding(ab) && isInScope(ab)) { + String vsRef = ab.getValueSet(); + ValueSet valueset = resolveBindingReference(profile, vsRef, profile.getUrl(), profile); + BindingStrength strength = convertPurposeToStrength(ab.getPurpose()); + + ok = validateBindingCodedElement(errors, path, element, profile, stack, theCode, theSystem, ok, checked, c, vsRef, valueset, strength, null, false) && ok; } } - } else if (binding.hasValueSet()) { - hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_CANTCHECK); - } else if (!inCodeableConcept && !noBindingMsgSuppressed) { - hint(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSOURCE, path); + } catch (Exception e) { + if (STACK_TRACE) e.printStackTrace(); + warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODING1, e.getMessage()); } } } @@ -2065,7 +2014,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_ERROR_CODING2, e.getMessage(), e.toString()); ok = false; } - if (!checked) { + if (!checked.ok()) { ok = rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, theSystem == null || isCodeSystemReferenceValid(theSystem), I18nConstants.TERMINOLOGY_TX_SYSTEM_RELATIVE) && ok; ok = rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, !isValueSet(theSystem), I18nConstants.TERMINOLOGY_TX_SYSTEM_VALUESET2, theSystem) && ok; if (ok) { @@ -2076,6 +2025,71 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return ok; } + private boolean validateBindingCodedElement(List errors, String path, Element element, + StructureDefinition profile, NodeStack stack, String theCode, String theSystem, boolean ok, BooleanHolder checked, + Coding c, String vsRef, ValueSet valueset, BindingStrength strength, Extension vsMax, boolean base) { + if (valueset == null) { + CodeSystem cs = context.fetchCodeSystem(vsRef); + if (rule(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, cs == null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND_CS, describeReference(vsRef))) { + warning(errors, NO_RULE_DATE, IssueType.CODEINVALID, element.line(), element.col(), path, valueset != null, I18nConstants.TERMINOLOGY_TX_VALUESET_NOTFOUND, describeReference(vsRef)); + } else { + ok = false; + } + } else { + BindingContext bc = base ? BindingContext.BASE : BindingContext.ADDITIONAL; + long t = System.nanoTime(); + ValidationResult vr = null; + if (strength != BindingStrength.EXAMPLE) { + checked.set(true); + vr = checkCodeOnServer(stack, valueset, c); + } + ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(strength), strength == BindingStrength.EXTENSIBLE, vsRef) && ok; + + timeTracker.tx(t, "vc "+c.getSystem()+"#"+c.getCode()+" '"+c.getDisplay()+"'"); + if (strength == BindingStrength.REQUIRED) { + removeTrackedMessagesForLocation(errors, element, path); + } + + if (vr != null && !vr.isOk()) { + if (vr.IsNoService()) + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_BINDING_NOSERVER, theSystem+"#"+theCode); + else if (vr.getErrorClass() != null && !vr.getErrorClass().isInfrastructure()) { + if (strength == BindingStrength.REQUIRED) + ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_4a, describeReference(vsRef, valueset, bc), vr.getMessage(), theSystem+"#"+theCode) && ok; + else if (strength == BindingStrength.EXTENSIBLE) { + if (vsMax != null) + checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack); + else if (!noExtensibleWarnings) + txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_5, describeReference(vsRef, valueset, bc), theSystem+"#"+theCode); + } else if (strength == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_CONFIRM_6, describeReference(vsRef, valueset, bc), theSystem+"#"+theCode); + } + } + } else if (strength == BindingStrength.REQUIRED) + ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_12, describeReference(vsRef, valueset, bc), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode) && ok; + else if (strength == BindingStrength.EXTENSIBLE) { + if (vsMax != null) + ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringFromExtension(vsMax), c, stack) && ok; + else if (!noExtensibleWarnings) { + txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_13, describeReference(vsRef, valueset, bc), getErrorMessage(vr.getMessage()), c.getSystem()+"#"+c.getCode()); + } + } else if (strength == BindingStrength.PREFERRED) { + if (baseOnly) { + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_14, describeReference(vsRef, valueset, bc), getErrorMessage(vr.getMessage()), theSystem+"#"+theCode); + } + } + } else if (vr != null && vr.getMessage() != null) { + if (vr.getSeverity() == IssueSeverity.INFORMATION) { + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } else { + txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); + } + } + } + return ok; + } + private boolean isValueSet(String url) { try { ValueSet vs = context.fetchResourceWithException(ValueSet.class, url); @@ -3579,7 +3593,7 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } vr = checkCodeOnServer(stack, vs, value, options); } - ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding), binding.getStrength() != BindingStrength.REQUIRED, binding.getValueSet()) && ok; + ok = processTxIssues(errors, vr, element, path, notFoundSeverityForBinding(binding.getStrength()), binding.getStrength() != BindingStrength.REQUIRED, binding.getValueSet()) && ok; timeTracker.tx(t, "vc "+value+""); if (binding.getStrength() == BindingStrength.REQUIRED) { @@ -3591,15 +3605,15 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat } else if (vr.getErrorClass() != null && vr.getErrorClass() == TerminologyServiceErrorClass.CODESYSTEM_UNSUPPORTED) { txWarning(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, vr.getMessage()); } else if (binding.getStrength() == BindingStrength.REQUIRED) { - ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_16, value, describeReference(binding.getValueSet(), vs), getErrorMessage(vr.getMessage())) && ok; + ok = txRule(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_16, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE), getErrorMessage(vr.getMessage())) && ok; } else if (binding.getStrength() == BindingStrength.EXTENSIBLE) { if (binding.hasExtension(ToolingExtensions.EXT_MAX_VALUESET)) ok = checkMaxValueSet(errors, path, element, profile, ToolingExtensions.readStringExtension(binding, ToolingExtensions.EXT_MAX_VALUESET), value, stack) && ok; else if (!noExtensibleWarnings && !isOkExtension(value, vs)) - txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_17, value, describeReference(binding.getValueSet(), vs), getErrorMessage(vr.getMessage())); + txWarningForLaterRemoval(element, errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_17, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE), getErrorMessage(vr.getMessage())); } else if (binding.getStrength() == BindingStrength.PREFERRED) { if (baseOnly) { - txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_18, value, describeReference(binding.getValueSet(), vs), getErrorMessage(vr.getMessage())); + txHint(errors, NO_RULE_DATE, vr.getTxLink(), IssueType.CODEINVALID, element.line(), element.col(), path, false, I18nConstants.TERMINOLOGY_TX_NOVALID_18, value, describeReference(binding.getValueSet(), vs, BindingContext.BASE), getErrorMessage(vr.getMessage())); } } } else if (vr != null && vr.getMessage() != null){ @@ -4311,21 +4325,28 @@ public class InstanceValidator extends BaseValidator implements IResourceValidat return false; } - private String describeReference(String reference, CanonicalResource target) { + private String describeReference(String reference, CanonicalResource target, BindingContext ctxt) { if (reference == null && target == null) return "null"; + String res = null; if (reference == null) { - return target.getVersionedUrl(); + res = target.getVersionedUrl(); + } else if (target == null) { + res = reference; + } else { + String uref = reference.contains("|") ? reference.substring(0, reference.lastIndexOf("|")) : reference; + String vref = reference.contains("|") ? reference.substring(reference.lastIndexOf("|")+1) : null; + if (uref.equals(target.getUrl()) && (vref == null || vref.equals(target.getVersion()))) { + res = "'"+target.present()+"' ("+target.getVersionedUrl()+")"; + } else { + res = reference + "(which actually refers to '"+target.present()+"' (" + target.getVersionedUrl() + "))"; + } } - if (target == null) { - return reference; + switch (ctxt) { + case ADDITIONAL: return context.formatMessage(I18nConstants.BINDING_ADDITIONAL, res); + case MAXVS: return context.formatMessage(I18nConstants.BINDING_MAX, res); + default: return res; } - String uref = reference.contains("|") ? reference.substring(0, reference.lastIndexOf("|")) : reference; - String vref = reference.contains("|") ? reference.substring(reference.lastIndexOf("|")+1) : null; - if (uref.equals(target.getUrl()) && (vref == null || vref.equals(target.getVersion()))) { - return "'"+target.present()+"' ("+target.getVersionedUrl()+")"; - } - return reference + "(which actually refers to '"+target.present()+"' (" + target.getVersionedUrl() + "))"; } private String describeTypes(List types) { From 29442c0b1d9bd1a9a8a535b2befafd9ebf4f1799 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 18 May 2024 03:25:23 -0500 Subject: [PATCH 07/14] fix tx tester calling run ok when a filter is applied and tests fail --- .../src/main/java/org/hl7/fhir/validation/special/TxTester.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java index 010a95059..d48a135a3 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTester.java @@ -100,7 +100,7 @@ public class TxTester { for (JsonObject suite : tests.getJsonObjects("suites")) { if ((!suite.has("mode") || modes.contains(suite.asString("mode")))) { if (suite.asBoolean("disabled")) { - ok = true; + // ok = true; } else { ok = runSuite(suite, tx, modes, filter, json.forceArray("suites")) && ok; } From 0645c79eb6c92ba539aee12cf6dd1674063998cd Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 18 May 2024 03:25:34 -0500 Subject: [PATCH 08/14] add testing ontoserver to test suite --- .../terminology/tests/OntoserverTests.java | 169 ++++++++++++++++++ 1 file changed, 169 insertions(+) create mode 100644 org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/OntoserverTests.java diff --git a/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/OntoserverTests.java b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/OntoserverTests.java new file mode 100644 index 000000000..3aee51e4d --- /dev/null +++ b/org.hl7.fhir.validation/src/test/java/org/hl7/fhir/terminology/tests/OntoserverTests.java @@ -0,0 +1,169 @@ +package org.hl7.fhir.terminology.tests; + +import java.io.File; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; +import java.util.*; + +import org.apache.commons.io.IOUtils; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_10_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_14_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_30_50; +import org.hl7.fhir.convertors.factory.VersionConvertorFactory_40_50; +import org.hl7.fhir.exceptions.DefinitionException; +import org.hl7.fhir.exceptions.FHIRException; +import org.hl7.fhir.exceptions.FHIRFormatError; +import org.hl7.fhir.r5.formats.JsonParser; +import org.hl7.fhir.r5.formats.XmlParser; +import org.hl7.fhir.r5.model.Constants; +import org.hl7.fhir.r5.model.Resource; +import org.hl7.fhir.r5.test.utils.TestingUtilities; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.filesystem.ManagedFileAccess; +import org.hl7.fhir.utilities.json.model.JsonObject; +import org.hl7.fhir.utilities.settings.FhirSettings; +import org.hl7.fhir.utilities.tests.TestConfig; +import org.hl7.fhir.validation.special.TxTester; +import org.hl7.fhir.validation.special.TxTester.ITxTesterLoader; +import org.hl7.fhir.validation.tests.utilities.TestUtilities; +import org.junit.Test; +import org.junit.jupiter.api.Assertions; +import org.junit.runner.RunWith; +import org.junit.runners.Parameterized; +import org.junit.runners.Parameterized.Parameters; + +import com.google.common.base.Charsets; + +@RunWith(Parameterized.class) + +public class OntoserverTests implements ITxTesterLoader { + + public static class JsonObjectPair { + public JsonObjectPair(JsonObject suite, JsonObject test) { + this.suite = suite; + this.test = test; + } + private JsonObject suite; + private JsonObject test; + } + + private static final String SERVER = "https://tx.ontoserver.csiro.au/fhir"; + + private static boolean localTxRunning() throws IOException { + return ManagedFileAccess.file("/Users/grahamegrieve/work/server/server").exists(); + } + + + + @Parameters(name = "{index}: id {0}") + public static Iterable data() throws IOException { + + String contents = TestingUtilities.loadTestResource("tx", "test-cases.json"); + externals = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(TestingUtilities.loadTestResource("tx", "messages-tx.fhir.org.json")); + + Map examples = new HashMap(); + manifest = org.hl7.fhir.utilities.json.parser.JsonParser.parseObject(contents); + for (org.hl7.fhir.utilities.json.model.JsonObject suite : manifest.getJsonObjects("suites")) { + String sn = suite.asString("name"); + for (org.hl7.fhir.utilities.json.model.JsonObject test : suite.getJsonObjects("tests")) { + String tn = test.asString("name"); + examples.put(sn+"."+tn, new JsonObjectPair(suite, test)); + } + } + + List names = new ArrayList(examples.size()); + names.addAll(examples.keySet()); + Collections.sort(names); + + List objects = new ArrayList(examples.size()); + for (String id : names) { + objects.add(new Object[]{id, examples.get(id)}); + } + return objects; + } + + private static org.hl7.fhir.utilities.json.model.JsonObject manifest; + private static org.hl7.fhir.utilities.json.model.JsonObject externals; + private JsonObjectPair setup; + private String version = "5.0.0"; + private static TxTester tester; + private List modes = new ArrayList<>(); + + public OntoserverTests(String name, JsonObjectPair setup) { + this.setup = setup; + modes.add("flat"); + } + + @SuppressWarnings("deprecation") + @Test + public void test() throws Exception { + if (TestUtilities.runningAsSurefire()) { + logTestSkip("Running in surefire."); + return; + } + if (!localTxRunning()) { + logTestSkip("No local terminology server available."); + return; + } + if (tester == null) { + tester = new TxTester(this, SERVER, true, externals); + } + String err = tester.executeTest(setup.suite, setup.test, modes); + Assertions.assertTrue(true); // we don't care what the result is, only that we didn't crash + + } + + private void logTestSkip(String reason) { + System.out.println("Skipping test: " + setup.suite.asString("name") + " " + setup.test.asString("name") + " reason: " + reason); + } + + public Resource loadResource(String filename) throws IOException, FHIRFormatError, FileNotFoundException, FHIRException, DefinitionException { + String contents = TestingUtilities.loadTestResource("tx", filename); + Resource res = null; + try (InputStream inputStream = IOUtils.toInputStream(contents, Charsets.UTF_8)) { + if (filename.contains(".json")) { + if (Constants.VERSION.equals(version) || "5.0".equals(version)) + res = new JsonParser().parse(inputStream); + else if (org.hl7.fhir.dstu3.model.Constants.VERSION.equals(version) || "3.0".equals(version)) + res = VersionConvertorFactory_30_50.convertResource(new org.hl7.fhir.dstu3.formats.JsonParser().parse(inputStream)); + else if (org.hl7.fhir.dstu2016may.model.Constants.VERSION.equals(version) || "1.4".equals(version)) + res = VersionConvertorFactory_14_50.convertResource(new org.hl7.fhir.dstu2016may.formats.JsonParser().parse(inputStream)); + else if (org.hl7.fhir.dstu2.model.Constants.VERSION.equals(version) || "1.0".equals(version)) + res = VersionConvertorFactory_10_50.convertResource(new org.hl7.fhir.dstu2.formats.JsonParser().parse(inputStream)); + else if (org.hl7.fhir.r4.model.Constants.VERSION.equals(version) || "4.0".equals(version)) + res = VersionConvertorFactory_40_50.convertResource(new org.hl7.fhir.r4.formats.JsonParser().parse(inputStream)); + else + throw new FHIRException("unknown version " + version); + } else { + if (Constants.VERSION.equals(version) || "5.0".equals(version)) + res = new XmlParser().parse(inputStream); + else if (org.hl7.fhir.dstu3.model.Constants.VERSION.equals(version) || "3.0".equals(version)) + res = VersionConvertorFactory_30_50.convertResource(new org.hl7.fhir.dstu3.formats.XmlParser().parse(inputStream)); + else if (org.hl7.fhir.dstu2016may.model.Constants.VERSION.equals(version) || "1.4".equals(version)) + res = VersionConvertorFactory_14_50.convertResource(new org.hl7.fhir.dstu2016may.formats.XmlParser().parse(inputStream)); + else if (org.hl7.fhir.dstu2.model.Constants.VERSION.equals(version) || "1.0".equals(version)) + res = VersionConvertorFactory_10_50.convertResource(new org.hl7.fhir.dstu2.formats.XmlParser().parse(inputStream)); + else if (org.hl7.fhir.r4.model.Constants.VERSION.equals(version) || "4.0".equals(version)) + res = VersionConvertorFactory_40_50.convertResource(new org.hl7.fhir.r4.formats.XmlParser().parse(inputStream)); + else + throw new FHIRException("unknown version " + version); + } + } + org.hl7.fhir.r4.model.Resource r4 = VersionConvertorFactory_40_50.convertResource(res); + String p = Utilities.path(FhirSettings.getFhirTestCasesPath(), "tx", "r4", filename); + Utilities.createDirectory(Utilities.getDirectoryForFile(p)); + new org.hl7.fhir.r4.formats.JsonParser().compose(ManagedFileAccess.outStream(p), r4); + return res; + } + + @Override + public String describe() { + return "Test cases"; + } + + @Override + public byte[] loadContent(String filename) throws FileNotFoundException, IOException { + return TestingUtilities.loadTestResourceBytes("tx", filename); + } +} \ No newline at end of file From 5956da771e2f1a2d54023075a456c82b3e5d1cbb Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 18 May 2024 03:25:46 -0500 Subject: [PATCH 09/14] fix NPE testing Ontoserver --- .../org/hl7/fhir/validation/special/TxTesterSorters.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java index 12609e7f1..35d069e8a 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/special/TxTesterSorters.java @@ -195,7 +195,6 @@ public class TxTesterSorters { } return res; } - } @@ -220,9 +219,9 @@ public class TxTesterSorters { } } if (o1.getName().equals(o2.getName()) && o1.getName().equals("designation")) { - String code1 = o1.getPart("language").hasValue() ? o1.getPart("language").getValue().primitiveValue().toLowerCase() : ""; - String code2 = o2.getPart("language").hasValue() ? o2.getPart("language").getValue().primitiveValue().toLowerCase() : ""; - if (code1 != null && code2 != null && !code1.equals(code2)) { + String code1 = o1.hasPart("language") && o1.getPart("language").hasValue() && o1.getPart("language").getValue().primitiveValue() != null ? o1.getPart("language").getValue().primitiveValue().toLowerCase() : ""; + String code2 = o2.hasPart("language") && o2.getPart("language").hasValue() && o2.getPart("language").getValue().primitiveValue() != null ? o2.getPart("language").getValue().primitiveValue().toLowerCase() : ""; + if (code1 != null && code2 != null && !code1.equals(code2)) { return code1.compareTo(code2); } String v1 = o1.getPart("value") != null && o1.getPart("value").hasPrimitiveValue() ? o1.getPart("value").getValue().primitiveValue().toLowerCase() : null; From f15f9f394e35785867bde82bfb035dc7a68be8f0 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 18 May 2024 07:36:30 -0500 Subject: [PATCH 10/14] Refactor system specific checking of value set filters --- .../fhir/utilities/i18n/I18nConstants.java | 2 +- .../src/main/resources/Messages.properties | 4 +- .../validation/codesystem/BCP47Checker.java | 39 ++ .../validation/codesystem/CPTChecker.java | 46 +++ .../codesystem/CodeSystemBasedChecker.java | 97 +++++ .../codesystem/CodeSystemChecker.java | 60 ++- .../codesystem/GeneralCodeSystemChecker.java | 3 +- .../validation/codesystem/LoincChecker.java | 220 ++++++++++ .../validation/codesystem/RxNormChecker.java | 61 +++ .../codesystem/SnomedCTChecker.java | 162 ++++++++ .../validation/codesystem/UcumChecker.java | 32 ++ .../instance/type/ValueSetValidator.java | 381 ++---------------- .../org.hl7.fhir.validation/4.0.1/loinc.cache | 65 ++- .../org.hl7.fhir.validation/5.0.0/loinc.cache | 65 ++- 14 files changed, 890 insertions(+), 347 deletions(-) create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/BCP47Checker.java create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CPTChecker.java create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemBasedChecker.java create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/LoincChecker.java create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/RxNormChecker.java create mode 100644 org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/UcumChecker.java 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 160a53bdd..da994d076 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 @@ -1093,7 +1093,7 @@ public class I18nConstants { public static final String VALUESET_BAD_FILTER_VALUE_DECIMAL = "VALUESET_BAD_FILTER_VALUE_DECIMAL"; public static final String VALUESET_BAD_FILTER_VALUE_INTEGER = "VALUESET_BAD_FILTER_VALUE_INTEGER"; public static final String VALUESET_BAD_FILTER_VALUE_VALID_CODE = "VALUESET_BAD_FILTER_VALUE_VALID_CODE"; - public static final String VALUESET_BAD_FILTER_VALUE_VALID_CODE_LOINC = "VALUESET_BAD_FILTER_VALUE_VALID_CODE_LOINC"; + public static final String VALUESET_BAD_FILTER_VALUE_VALID_CODE_CHANGE = "VALUESET_BAD_FILTER_VALUE_VALID_CODE_CHANGE"; public static final String VALUESET_BAD_FILTER_VALUE_CODED = "VALUESET_BAD_FILTER_VALUE_CODED"; public static final String VALUESET_BAD_FILTER_VALUE_CODED_INVALID = "VALUESET_BAD_FILTER_VALUE_CODED_INVALID"; public static final String VALUESET_BAD_FILTER_OP = "VALUESET_BAD_FILTER_OP"; diff --git a/org.hl7.fhir.utilities/src/main/resources/Messages.properties b/org.hl7.fhir.utilities/src/main/resources/Messages.properties index 6c76d8fe1..8ccfd2e54 100644 --- a/org.hl7.fhir.utilities/src/main/resources/Messages.properties +++ b/org.hl7.fhir.utilities/src/main/resources/Messages.properties @@ -1132,10 +1132,10 @@ VALUESET_BAD_FILTER_VALUE_DATETIME = The value for a filter based on property '' VALUESET_BAD_FILTER_VALUE_DECIMAL = The value for a filter based on property ''{0}'' must be a decimal value, not ''{1}'' VALUESET_BAD_FILTER_VALUE_INTEGER = The value for a filter based on property ''{0}'' must be integer value, not ''{1}'' VALUESET_BAD_FILTER_VALUE_VALID_CODE = The value for a filter based on property ''{0}'' must be a valid code from the system ''{2}'', and ''{1}'' is not ({3}) -VALUESET_BAD_FILTER_VALUE_VALID_CODE_LOINC = The value for a filter based on property ''{0}'' looks like a LOINC code, but ''{3}'' is not valid code in LOINC version ''{1}'' +VALUESET_BAD_FILTER_VALUE_VALID_CODE_CHANGE = The value for a filter based on property ''{0}'' must be a valid code from the system ''{2}'', and ''{1}'' is not ({3}). Note that this is change from the past; terminology servers are expected to still continue to support this filter VALUESET_BAD_FILTER_VALUE_CODED = The value for a filter based on property ''{0}'' must be in the format system(|version)#code, not ''{1}'' VALUESET_BAD_FILTER_VALUE_CODED_INVALID = The value for a filter based on property ''{0}'' is ''{1}'' which is not a valid code ({2}) -VALUESET_BAD_FILTER_OP = The operation ''{0}'' is not allowed for property ''{1}''. Allowed ops: {2} +VALUESET_BAD_FILTER_OP = The operation ''{0}'' is not allowed for property ''{1}'' in system ''{3}''. Allowed ops: {2} VALUESET_BAD_FILTER_VALUE_HAS_COMMA = The filter value has a comma, but the operation is different to 'in' and 'not-in', so the comma will be interpreted as part of the {0} value VALUESET_BAD_FILTER_VALUE_VALID_REGEX = The value for a filter based on property ''{0}'' should be a valid regex, not ''{1}'' (err = ''{2}'') VALUESET_BAD_PROPERTY_NO_REGEX = Cannot apply a regex filter to the property ''{0}'' (usually regex filters are applied to the codes, or a named property of the code system) diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/BCP47Checker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/BCP47Checker.java new file mode 100644 index 000000000..9049f4544 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/BCP47Checker.java @@ -0,0 +1,39 @@ +package org.hl7.fhir.validation.codesystem; + +import java.util.EnumSet; +import java.util.List; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyOperation; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyValidationRules; + +public class BCP47Checker extends CodeSystemChecker { + + public BCP47Checker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, List errors) { + super(context, xverManager, debug, errors); + } + + + @Override + public void listPropertyNames(List knownNames) { + super.listPropertyNames(knownNames); + addName(knownNames, "language"); + addName(knownNames, "region"); + addName(knownNames, "script"); + addName(knownNames, "variant"); + addName(knownNames, "extension"); + addName(knownNames, "ext-lang"); + addName(knownNames, "private-use"); + } + + + @Override + public PropertyValidationRules rulesForFilter(String property, EnumSet ops) { + // TODO Auto-generated method stub + return super.rulesForFilter(property, ops); + } + + +} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CPTChecker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CPTChecker.java new file mode 100644 index 000000000..07a0d9235 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CPTChecker.java @@ -0,0 +1,46 @@ +package org.hl7.fhir.validation.codesystem; + +import java.util.EnumSet; +import java.util.List; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent; +import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent; +import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.CodeValidationRule; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyFilterType; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyOperation; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyValidationRules; + +public class CPTChecker extends CodeSystemChecker { + + public CPTChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, List errors) { + super(context, xverManager, debug, errors); + } + + @Override + public void listPropertyNames(List knownNames) { + super.listPropertyNames(knownNames); + addName(knownNames, "modifier"); + addName(knownNames, "kind"); + addName(knownNames, "modified"); + addName(knownNames, "code"); + addName(knownNames, "telemedicine"); + addName(knownNames, "orthopox"); + } + + @Override + public PropertyValidationRules rulesForFilter(String property, EnumSet ops) { + switch (property) { + case "modifier": return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops); + case "kind" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); // for now + case "modified": return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops); + case "code" : return null; + case "telemedicine": return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops); + case "orthopox" : return new PropertyValidationRules(PropertyFilterType.Boolean,null, ops); + } + return null; + } + +} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemBasedChecker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemBasedChecker.java new file mode 100644 index 000000000..a55bf7048 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemBasedChecker.java @@ -0,0 +1,97 @@ +package org.hl7.fhir.validation.codesystem; + +import java.util.EnumSet; +import java.util.List; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.model.CodeSystem; +import org.hl7.fhir.r5.model.Enumeration; +import org.hl7.fhir.r5.model.CodeSystem.CodeSystemFilterComponent; +import org.hl7.fhir.r5.model.CodeSystem.PropertyComponent; +import org.hl7.fhir.r5.model.Enumerations.FilterOperator; +import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.CodeValidationRule; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyFilterType; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyOperation; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyValidationRules; + +public class CodeSystemBasedChecker extends CodeSystemChecker { + + private CodeSystem cs; + + public CodeSystemBasedChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, List errors, CodeSystem cs) { + super(context, xverManager, debug, errors); + this.cs = cs; + } + + @Override + public void listPropertyNames(List knownNames) { + super.listPropertyNames(knownNames); + if (cs.hasHierarchyMeaning()) { + knownNames.add("parent"); + knownNames.add("child"); + knownNames.add("partOf"); + } + for (CodeSystemFilterComponent f : cs.getFilter()) { + addName(knownNames, f.getCode()); + } + for (PropertyComponent p : cs.getProperty()) { + addName(knownNames, p.getCode()); + } + } + + @Override + public PropertyValidationRules rulesForFilter(String property, EnumSet ops) { + + for (CodeSystemFilterComponent f : cs.getFilter()) { + if (property.equals(f.getCode())) { + for (Enumeration op : f.getOperator()) { + ops.add(toOp(op)); + } + } + } + + for (PropertyComponent p : cs.getProperty()) { + if (property.equals(p.getCode())) { + if (p.getType() != null) { + switch (p.getType()) { + case BOOLEAN: return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops); + case CODE: + // the definitions say " a code that identifies a concept defined in the code system" -> ValidCode. + // but many people have ignored that and defined a property as 'code' because it's from a list of values that are otherwise undefined + boolean external = !forPublication || cs.getWebPath() == null || Utilities.isAbsoluteUrl(cs.getWebPath()); + return new PropertyValidationRules(PropertyFilterType.Code, external ? CodeValidationRule.Warning : CodeValidationRule.Error, ops); // valid code... the definitions say that, but people were missing that in the pastm + case CODING: return new PropertyValidationRules(PropertyFilterType.Coding, null, ops); + case DATETIME: return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops); + case DECIMAL: return new PropertyValidationRules(PropertyFilterType.Decimal, null, ops); + case INTEGER: return new PropertyValidationRules(PropertyFilterType.Integer, null, ops); + case STRING: return null; + } + } + } + } + + return super.rulesForFilter(property, ops); + } + + + private PropertyOperation toOp(Enumeration op) { + switch (op.getValue()) { + case CHILDOF: return PropertyOperation.ChildOf; + case DESCENDENTLEAF: return PropertyOperation.DescendentLeaf; + case DESCENDENTOF: return PropertyOperation.DescendentOf; + case EQUAL: return PropertyOperation.Equals; + case EXISTS: return PropertyOperation.Exists; + case GENERALIZES: return PropertyOperation.Generalizes; + case IN: return PropertyOperation.In; + case ISA: return PropertyOperation.IsA; + case ISNOTA: return PropertyOperation.IsNotA; + case NOTIN: return PropertyOperation.NotIn; + case REGEX: return PropertyOperation.RegEx; + default: return null; + } + } + +} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemChecker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemChecker.java index 9417547bb..9508896c3 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemChecker.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/CodeSystemChecker.java @@ -1,5 +1,6 @@ package org.hl7.fhir.validation.codesystem; +import java.util.EnumSet; import java.util.List; import org.hl7.fhir.r5.context.IWorkerContext; @@ -10,9 +11,13 @@ import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.validation.BaseValidator; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.CodeValidationRule; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyFilterType; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyOperation; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyValidationRules; import org.hl7.fhir.validation.instance.utils.NodeStack; -public class CodeSystemChecker extends BaseValidator { +public abstract class CodeSystemChecker extends BaseValidator { private boolean noDisplay = false; private boolean hasDisplay = false; @@ -33,6 +38,59 @@ public class CodeSystemChecker extends BaseValidator { public void finish(Element inc, NodeStack stack) { hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noDisplay && hasDisplay), I18nConstants.VALUESET_CONCEPT_DISPLAY_PRESENCE_MIXED); + } + + /** + * these are true in all code systems + * + * @param knownNames + */ + public void listPropertyNames(List knownNames) { + knownNames.add("concept"); + knownNames.add("code"); + knownNames.add("status"); + knownNames.add("inactive"); + knownNames.add("effectiveDate"); + knownNames.add("deprecationDate"); + knownNames.add("retirementDate"); + knownNames.add("notSelectable"); + knownNames.add("synonym"); + knownNames.add("comment"); + knownNames.add("itemWeight"); } + + protected void addName(List knownNames, String code) { + if (code != null && !knownNames.contains(code)) { + knownNames.add(code); + } + } + + protected EnumSet addToOps(EnumSet set, PropertyOperation... ops) { + for (PropertyOperation op : ops) { + set.add(op); + } + return set; + } + + public PropertyValidationRules rulesForFilter(String property, EnumSet ops) { + switch (property) { + case "concept" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In, PropertyOperation.IsA, PropertyOperation.DescendentOf, PropertyOperation.DescendentLeaf, PropertyOperation.IsNotA, PropertyOperation.NotIn)); + case "code" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx)); + case "status" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); + case "inactive" : return new PropertyValidationRules(PropertyFilterType.Boolean,null, ops); + case "effectiveDate" : return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops); + case "deprecationDate" : return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops); + case "retirementDate" : return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops); + case "notSelectable" : return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops); + case "parent" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, ops); + case "child" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, ops); + case "partOf" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, ops); + case "synonym" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); // ? none? + case "comment" : return null; + case "itemWeight" : return new PropertyValidationRules(PropertyFilterType.Decimal, null, ops); + } + return null; + } + } \ No newline at end of file diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/GeneralCodeSystemChecker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/GeneralCodeSystemChecker.java index d8ea86f15..a314eea22 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/GeneralCodeSystemChecker.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/GeneralCodeSystemChecker.java @@ -8,8 +8,7 @@ import org.hl7.fhir.utilities.validation.ValidationMessage; public class GeneralCodeSystemChecker extends CodeSystemChecker { - public GeneralCodeSystemChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, - List errors) { + public GeneralCodeSystemChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, List errors) { super(context, xverManager, debug, errors); // TODO Auto-generated constructor stub } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/LoincChecker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/LoincChecker.java new file mode 100644 index 000000000..0618af6b2 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/LoincChecker.java @@ -0,0 +1,220 @@ +package org.hl7.fhir.validation.codesystem; + +import java.util.EnumSet; +import java.util.List; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.CodeValidationRule; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyFilterType; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyOperation; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyValidationRules; + +public class LoincChecker extends CodeSystemChecker { + + public LoincChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, List errors) { + super(context, xverManager, debug, errors); + } + + + @Override + public void listPropertyNames(List knownNames) { + super.listPropertyNames(knownNames); + addName(knownNames, "concept"); + addName(knownNames, "ancestor"); + addName(knownNames, "descendent"); + addName(knownNames, "parent"); + addName(knownNames, "child"); + addName(knownNames, "COMPONENT"); + addName(knownNames, "PROPERTY"); + addName(knownNames, "TIME_ASPCT"); + addName(knownNames, "SYSTEM"); + addName(knownNames, "SCALE_TYP"); + addName(knownNames, "METHOD_TYP"); + addName(knownNames, "CLASS"); + addName(knownNames, "VersionLastChanged"); + addName(knownNames, "CHNG_TYPE"); + addName(knownNames, "DefinitionDescription"); + addName(knownNames, "STATUS"); + addName(knownNames, "CLASSTYPE"); + addName(knownNames, "FORMULA"); + addName(knownNames, "EXMPL_ANSWERS"); + addName(knownNames, "SURVEY_QUEST_TEXT"); + addName(knownNames, "SURVEY_QUEST_SRC"); + addName(knownNames, "UNITSREQUIRED"); + addName(knownNames, "ORDER_OBS"); + addName(knownNames, "HL7_FIELD_SUBFIELD_ID"); + addName(knownNames, "EXTERNAL_COPYRIGHT_NOTICE"); + addName(knownNames, "EXAMPLE_UNITS"); + addName(knownNames, "EXAMPLE_UCUM_UNITS"); + addName(knownNames, "STATUS_REASON"); + addName(knownNames, "STATUS_TEXT"); + addName(knownNames, "CHANGE_REASON_PUBLIC"); + addName(knownNames, "COMMON_TEST_RANK"); + addName(knownNames, "COMMON_ORDER_RANK"); + addName(knownNames, "HL7_ATTACHMENT_STRUCTURE"); + addName(knownNames, "EXTERNAL_COPYRIGHT_LINK"); + addName(knownNames, "PanelType"); + addName(knownNames, "AskAtOrderEntry"); + addName(knownNames, "AssociatedObservations"); + addName(knownNames, "VersionFirstReleased"); + addName(knownNames, "ValidHL7AttachmentRequest"); + addName(knownNames, "answer-list"); + addName(knownNames, "MAP_TO"); + addName(knownNames, "analyte"); + addName(knownNames, "analyte-core"); + addName(knownNames, "analyte-suffix"); + addName(knownNames, "analyte-numerator"); + addName(knownNames, "analyte-divisor"); + addName(knownNames, "analyte-divisor-suffix"); + addName(knownNames, "challenge"); + addName(knownNames, "adjustment"); + addName(knownNames, "count"); + addName(knownNames, "time-core"); + addName(knownNames, "time-modifier"); + addName(knownNames, "system-core"); + addName(knownNames, "super-system"); + addName(knownNames, "analyte-gene"); + addName(knownNames, "category"); + addName(knownNames, "search"); + addName(knownNames, "rad-modality-modality-type"); + addName(knownNames, "rad-modality-modality-subtype"); + addName(knownNames, "rad-anatomic-location-region-imaged"); + addName(knownNames, "rad-anatomic-location-imaging-focus"); + addName(knownNames, "rad-anatomic-location-laterality-presence"); + addName(knownNames, "rad-anatomic-location-laterality"); + addName(knownNames, "rad-view-aggregation"); + addName(knownNames, "rad-view-view-type"); + addName(knownNames, "rad-maneuver-maneuver-type"); + addName(knownNames, "rad-timing"); + addName(knownNames, "rad-pharmaceutical-substance-given"); + addName(knownNames, "rad-pharmaceutical-route"); + addName(knownNames, "rad-reason-for-exam"); + addName(knownNames, "rad-guidance-for-presence"); + addName(knownNames, "rad-guidance-for-approach"); + addName(knownNames, "rad-guidance-for-action"); + addName(knownNames, "rad-guidance-for-object"); + addName(knownNames, "rad-subject"); + addName(knownNames, "document-kind"); + addName(knownNames, "document-role"); + addName(knownNames, "document-setting"); + addName(knownNames, "document-subject-matter-domain"); + addName(knownNames, "document-type-of-service"); + addName(knownNames, "answers-for"); + addName(knownNames, "answer"); + addName(knownNames, "answer-list"); + + } + + @Override + public PropertyValidationRules rulesForFilter(String property, EnumSet ops) { + if (Utilities.existsInList(property, + "ancestor", + "descendent", + "parent", + "child", + "COMPONENT", + "PROPERTY", + "TIME_ASPCT", + "SYSTEM", + "SCALE_TYP", + "METHOD_TYP", + "CLASS", + "answer-list", + "MAP_TO", + "analyte", + "analyte-core", + "analyte-suffix", + "analyte-numerator", + "analyte-divisor", + "analyte-divisor-suffix", + "challenge", + "adjustment", + "count", + "time-core", + "time-modifier", + "system-core", + "super-system", + "analyte-gene", + "category", + "search", + "rad-modality-modality-type", + "rad-modality-modality-subtype", + "rad-anatomic-location-region-imaged", + "rad-anatomic-location-imaging-focus", + "rad-anatomic-location-laterality-presence", + "rad-anatomic-location-laterality", + "rad-view-aggregation", + "rad-view-view-type", + "rad-maneuver-maneuver-type", + "rad-timing", + "rad-pharmaceutical-substance-given", + "rad-pharmaceutical-route", + "rad-reason-for-exam", + "rad-guidance-for-presence", + "rad-guidance-for-approach", + "rad-guidance-for-action", + "rad-guidance-for-object", + "rad-subject", + "document-kind", + "document-role", + "document-setting", + "document-subject-matter-domain", + "document-type-of-service", + "answers-for", + "answer", + "answer-list")) { + return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.Exists, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)).setChange(true); + } + + if (Utilities.existsInList(property, + "VersionLastChanged", + "CHNG_TYPE", + "DefinitionDescription", + "STATUS", + "CLASSTYPE", + "FORMULA", + "EXMPL_ANSWERS", + "SURVEY_QUEST_TEXT", + "SURVEY_QUEST_SRC", + "UNITSREQUIRED", + "ORDER_OBS", + "HL7_FIELD_SUBFIELD_ID", + "EXTERNAL_COPYRIGHT_NOTICE", + "EXAMPLE_UNITS", + "EXAMPLE_UCUM_UNITS", + "STATUS_REASON", + "STATUS_TEXT", + "CHANGE_REASON_PUBLIC", + "COMMON_TEST_RANK", + "COMMON_ORDER_RANK", + "HL7_ATTACHMENT_STRUCTURE", + "EXTERNAL_COPYRIGHT_LINK", + "PanelType", + "AskAtOrderEntry", + "AssociatedObservations", + "VersionFirstReleased", + "ValidHL7AttachmentRequest")) { + return new PropertyValidationRules(PropertyFilterType.String, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.Exists, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)); + } + + if (Utilities.existsInList(property, + "STATUS")) { + return new PropertyValidationRules(PropertyFilterType.CodeList, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)) + .setCodes("ACTIVE", "TRIAL", "DISCOURAGED", "DEPRECATED"); + } + + if (Utilities.existsInList(property, + "copyright")) { + return new PropertyValidationRules(PropertyFilterType.CodeList, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)).setCodes("LOINC", "3rdParty"); + } + + if (Utilities.existsInList(property, + "concept")) { + return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, addToOps(ops, PropertyOperation.IsA)); + } + return null; + } +} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/RxNormChecker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/RxNormChecker.java new file mode 100644 index 000000000..9de081831 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/RxNormChecker.java @@ -0,0 +1,61 @@ +package org.hl7.fhir.validation.codesystem; + +import java.util.EnumSet; +import java.util.List; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.CodeValidationRule; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyFilterType; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyOperation; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyValidationRules; + +public class RxNormChecker extends CodeSystemChecker { + + public RxNormChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, List errors) { + super(context, xverManager, debug, errors); + } + + + @Override + public void listPropertyNames(List knownNames) { + super.listPropertyNames(knownNames); + addName(knownNames, "STY"); + addName(knownNames, "SAB"); + addName(knownNames, "TTY"); + addName(knownNames, "SY"); + addName(knownNames, "SIB"); + addName(knownNames, "RN"); + addName(knownNames, "PAR"); + addName(knownNames, "CHD"); + addName(knownNames, "RB"); + addName(knownNames, "RO"); + addName(knownNames, "IN"); + addName(knownNames, "PIN"); + addName(knownNames, "MIN"); + addName(knownNames, "BN"); + addName(knownNames, "SCD"); + addName(knownNames, "SBD"); + addName(knownNames, "GPCK"); + addName(knownNames, "BPCK"); + addName(knownNames, "SCDC"); + addName(knownNames, "SCDF"); + addName(knownNames, "SCDFP"); + addName(knownNames, "SCDG"); + addName(knownNames, "SCDGP"); + addName(knownNames, "SBDC"); + addName(knownNames, "SBDF"); + addName(knownNames, "SBDFP"); + addName(knownNames, "SBDG"); + addName(knownNames, "DF"); + addName(knownNames, "DFG"); + + } + + @Override + public PropertyValidationRules rulesForFilter(String property, EnumSet ops) { + return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); + } + +} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/SnomedCTChecker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/SnomedCTChecker.java index 235ce5320..048f906d5 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/SnomedCTChecker.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/SnomedCTChecker.java @@ -1,6 +1,7 @@ package org.hl7.fhir.validation.codesystem; import java.util.ArrayList; +import java.util.EnumSet; import java.util.List; import org.hl7.fhir.r5.context.IWorkerContext; @@ -10,6 +11,10 @@ import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.CodeValidationRule; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyFilterType; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyOperation; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyValidationRules; import org.hl7.fhir.validation.instance.utils.NodeStack; public class SnomedCTChecker extends CodeSystemChecker { @@ -45,4 +50,161 @@ public class SnomedCTChecker extends CodeSystemChecker { super.finish(inc, stack); hint(errors, "2023-07-21", IssueType.BUSINESSRULE, inc.line(), inc.col(), stack.getLiteralPath(), !(noTag && hasTag), I18nConstants.VALUESET_CONCEPT_DISPLAY_SCT_TAG_MIXED, tags.toString(), noTags.toString()); } + + @Override + public void listPropertyNames(List knownNames) { + // list from http://tx.fhir.org/r4/ValueSet/$expand?url=http://snomed.info/sct?fhir_vs=isa/410662002 + addName(knownNames, "concept"); + addName(knownNames, "constraint"); + addName(knownNames, "expressions"); + addName(knownNames, "410662002"); + addName(knownNames, "42752001"); + addName(knownNames, "47429007"); + addName(knownNames, "116676008"); + addName(knownNames, "116686009"); + addName(knownNames, "118168003"); + addName(knownNames, "118169006"); + addName(knownNames, "118170007"); + addName(knownNames, "118171006"); + addName(knownNames, "127489000"); + addName(knownNames, "131195008"); + addName(knownNames, "246075003"); + addName(knownNames, "246090004"); + addName(knownNames, "246093002"); + addName(knownNames, "246112005"); + addName(knownNames, "246454002"); + addName(knownNames, "246456000"); + addName(knownNames, "246501002"); + addName(knownNames, "246513007"); + addName(knownNames, "246514001"); + addName(knownNames, "255234002"); + addName(knownNames, "260507000"); + addName(knownNames, "260686004"); + addName(knownNames, "260870009"); + addName(knownNames, "263502005"); + addName(knownNames, "272741003"); + addName(knownNames, "288556008"); + addName(knownNames, "363589002"); + addName(knownNames, "363698007"); + addName(knownNames, "363699004"); + addName(knownNames, "363700003"); + addName(knownNames, "363701004"); + addName(knownNames, "363702006"); + addName(knownNames, "363703001"); + addName(knownNames, "363704007"); + addName(knownNames, "363705008"); + addName(knownNames, "363709002"); + addName(knownNames, "363710007"); + addName(knownNames, "363713009"); + addName(knownNames, "363714003"); + addName(knownNames, "370129005"); + addName(knownNames, "370130000"); + addName(knownNames, "370131001"); + addName(knownNames, "370132008"); + addName(knownNames, "370133003"); + addName(knownNames, "370134009"); + addName(knownNames, "370135005"); + addName(knownNames, "371881003"); + addName(knownNames, "405813007"); + addName(knownNames, "405814001"); + addName(knownNames, "405815000"); + addName(knownNames, "405816004"); + addName(knownNames, "408729009"); + addName(knownNames, "408730004"); + addName(knownNames, "408731000"); + addName(knownNames, "408732007"); + addName(knownNames, "410675002"); + addName(knownNames, "411116001"); + addName(knownNames, "418775008"); + addName(knownNames, "419066007"); + addName(knownNames, "424226004"); + addName(knownNames, "424244007"); + addName(knownNames, "424361007"); + addName(knownNames, "424876005"); + addName(knownNames, "425391005"); + addName(knownNames, "609096000"); + addName(knownNames, "704319004"); + addName(knownNames, "704320005"); + addName(knownNames, "704321009"); + addName(knownNames, "704322002"); + addName(knownNames, "704323007"); + addName(knownNames, "704324001"); + addName(knownNames, "704325000"); + addName(knownNames, "704326004"); + addName(knownNames, "704327008"); + addName(knownNames, "704346009"); + addName(knownNames, "704347000"); + addName(knownNames, "704647008"); + addName(knownNames, "718497002"); + addName(knownNames, "719715003"); + addName(knownNames, "719722006"); + addName(knownNames, "726542003"); + addName(knownNames, "726633004"); + addName(knownNames, "732943007"); + addName(knownNames, "732945000"); + addName(knownNames, "732947008"); + addName(knownNames, "733722007"); + addName(knownNames, "733725009"); + addName(knownNames, "733928003"); + addName(knownNames, "733930001"); + addName(knownNames, "733931002"); + addName(knownNames, "733932009"); + addName(knownNames, "733933004"); + addName(knownNames, "734136001"); + addName(knownNames, "734137005"); + addName(knownNames, "736472000"); + addName(knownNames, "736473005"); + addName(knownNames, "736474004"); + addName(knownNames, "736475003"); + addName(knownNames, "736476002"); + addName(knownNames, "736518005"); + addName(knownNames, "738774007"); + addName(knownNames, "762705008"); + addName(knownNames, "762706009"); + addName(knownNames, "762949000"); + addName(knownNames, "762951001"); + addName(knownNames, "763032000"); + addName(knownNames, "766939001"); + addName(knownNames, "774081006"); + addName(knownNames, "774158006"); + addName(knownNames, "774159003"); + addName(knownNames, "774160008"); + addName(knownNames, "774163005"); + addName(knownNames, "827081001"); + addName(knownNames, "836358009"); + addName(knownNames, "840560000"); + addName(knownNames, "860779006"); + addName(knownNames, "860781008"); + addName(knownNames, "1003703000"); + addName(knownNames, "1003735000"); + addName(knownNames, "1142135004"); + addName(knownNames, "1142136003"); + addName(knownNames, "1142137007"); + addName(knownNames, "1142138002"); + addName(knownNames, "1142139005"); + addName(knownNames, "1142140007"); + addName(knownNames, "1142141006"); + addName(knownNames, "1142142004"); + addName(knownNames, "1142143009"); + addName(knownNames, "1148793005"); + addName(knownNames, "1148965004"); + addName(knownNames, "1148967007"); + addName(knownNames, "1148968002"); + addName(knownNames, "1148969005"); + addName(knownNames, "1149366004"); + addName(knownNames, "1149367008"); + addName(knownNames, "1230370004"); + addName(knownNames, "320091000221107"); + } + + @Override + public PropertyValidationRules rulesForFilter(String property, EnumSet ops) { + switch (property) { + case "constraint": return null; // for now + case "expressions": return new PropertyValidationRules(PropertyFilterType.Boolean, null, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In)); + case "concept": return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.IsA, PropertyOperation.IsNotA, PropertyOperation.In, PropertyOperation.DescendentOf, PropertyOperation.DescendentLeaf)); + default: + return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In)); + } + } } diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/UcumChecker.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/UcumChecker.java new file mode 100644 index 000000000..e18cf56f7 --- /dev/null +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/codesystem/UcumChecker.java @@ -0,0 +1,32 @@ +package org.hl7.fhir.validation.codesystem; + +import java.util.EnumSet; +import java.util.List; + +import org.hl7.fhir.r5.context.IWorkerContext; +import org.hl7.fhir.r5.utils.XVerExtensionManager; +import org.hl7.fhir.utilities.validation.ValidationMessage; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.CodeValidationRule; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyFilterType; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyOperation; +import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyValidationRules; + +public class UcumChecker extends CodeSystemChecker { + + public UcumChecker(IWorkerContext context, XVerExtensionManager xverManager, boolean debug, List errors) { + super(context, xverManager, debug, errors); + } + + + @Override + public void listPropertyNames(List knownNames) { + super.listPropertyNames(knownNames); + addName(knownNames, "property"); + addName(knownNames, "canonical"); + } + + @Override + public PropertyValidationRules rulesForFilter(String property, EnumSet ops) { + return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); + } +} diff --git a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java index e7aa21807..f5d570598 100644 --- a/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java +++ b/org.hl7.fhir.validation/src/main/java/org/hl7/fhir/validation/instance/type/ValueSetValidator.java @@ -29,9 +29,15 @@ import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; import org.hl7.fhir.utilities.validation.ValidationOptions; import org.hl7.fhir.validation.BaseValidator; +import org.hl7.fhir.validation.codesystem.BCP47Checker; +import org.hl7.fhir.validation.codesystem.CPTChecker; +import org.hl7.fhir.validation.codesystem.CodeSystemBasedChecker; import org.hl7.fhir.validation.codesystem.CodeSystemChecker; import org.hl7.fhir.validation.codesystem.GeneralCodeSystemChecker; +import org.hl7.fhir.validation.codesystem.LoincChecker; +import org.hl7.fhir.validation.codesystem.RxNormChecker; import org.hl7.fhir.validation.codesystem.SnomedCTChecker; +import org.hl7.fhir.validation.codesystem.UcumChecker; import org.hl7.fhir.validation.instance.InstanceValidator; import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyOperation; import org.hl7.fhir.validation.instance.type.ValueSetValidator.PropertyValidationRules; @@ -66,11 +72,12 @@ public class ValueSetValidator extends BaseValidator { } - public class PropertyValidationRules { + public static class PropertyValidationRules { private PropertyFilterType type; private CodeValidationRule codeValidation; private EnumSet ops; private List codeList = new ArrayList<>(); + private boolean change; protected PropertyValidationRules(PropertyFilterType type, CodeValidationRule codeValidation, PropertyOperation... ops) { super(); @@ -106,11 +113,18 @@ public class ValueSetValidator extends BaseValidator { } return this; } + public boolean isChange() { + return change; + } + public PropertyValidationRules setChange(boolean change) { + this.change = change; + return this; + } } public enum PropertyFilterType { - Boolean, Integer, Decimal, Code, DateTime, Coding, CodeList, String, LoincRelationship + Boolean, Integer, Decimal, Code, DateTime, Coding, CodeList, String } private static final int TOO_MANY_CODES_TO_VALIDATE = 1000; @@ -121,7 +135,18 @@ public class ValueSetValidator extends BaseValidator { } switch (system) { case "http://snomed.info/sct" :return new SnomedCTChecker(context, xverManager, debug, errors); - default: return new GeneralCodeSystemChecker(context, xverManager, debug, errors); + case "http://loinc.org": return new LoincChecker(context, xverManager, debug, errors); + case "http://www.nlm.nih.gov/research/umls/rxnorm": return new RxNormChecker(context, xverManager, debug, errors); + case "http://unitsofmeasure.org": return new UcumChecker(context, xverManager, debug, errors); + case "http://www.ama-assn.org/go/cpt": return new CPTChecker(context, xverManager, debug, errors); + case "urn:ietf:bcp:47": return new BCP47Checker(context, xverManager, debug, errors); + default: + CodeSystem cs = context.fetchCodeSystem(system); + if (cs != null) { + return new CodeSystemBasedChecker(context, xverManager, debug, errors, cs); + } else { + return new GeneralCodeSystemChecker(context, xverManager, debug, errors); + } } } @@ -262,7 +287,7 @@ public class ValueSetValidator extends BaseValidator { List concepts = include.getChildrenByName("concept"); List filters = include.getChildrenByName("filter"); - CodeSystemChecker slv = getSystemValidator(system, errors); + CodeSystemChecker csChecker = getSystemValidator(system, errors); CodeSystem cs = null; if (!Utilities.noString(system)) { cs = context.fetchCodeSystem(system, version); @@ -293,10 +318,10 @@ public class ValueSetValidator extends BaseValidator { for (Element concept : concepts) { // we treat the first differently because we want to know if the system is worth validating. if it is, then we batch the rest if (first) { - systemOk = validateValueSetIncludeConcept(errors, concept, stack, stack.push(concept, cc, null, null), system, version, slv); + systemOk = validateValueSetIncludeConcept(errors, concept, stack, stack.push(concept, cc, null, null), system, version, csChecker); first = false; } else if (systemOk) { - batch.add(prepareValidateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version, slv)); + batch.add(prepareValidateValueSetIncludeConcept(errors, concept, stack.push(concept, cc, null, null), system, version, csChecker)); } cc++; } @@ -330,10 +355,10 @@ public class ValueSetValidator extends BaseValidator { int cf = 0; for (Element filter : filters) { - ok = validateValueSetIncludeFilter(errors, filter, stack.push(filter, cf, null, null), system, version, cs, slv) & ok; + ok = validateValueSetIncludeFilter(errors, filter, stack.push(filter, cf, null, null), system, version, cs, csChecker) & ok; cf++; } - slv.finish(include, stack); + csChecker.finish(include, stack); } else { warning(errors, NO_RULE_DATE, IssueType.BUSINESSRULE, stack, filters.size() == 0 && concepts.size() == 0, I18nConstants.VALUESET_NO_SYSTEM_WARNING); } @@ -394,41 +419,16 @@ public class ValueSetValidator extends BaseValidator { return new VSCodingValidationRequest(stack, c); } - private boolean validateValueSetIncludeFilter(List errors, Element filter, NodeStack stack, String system, String version, CodeSystem cs, CodeSystemChecker slv) { + private boolean validateValueSetIncludeFilter(List errors, Element filter, NodeStack stack, String system, String version, CodeSystem cs, CodeSystemChecker csChecker) { boolean ok = true; String property = filter.getChildValue("property"); String op = filter.getChildValue("op"); String value = filter.getChildValue("value"); - + if (property != null) { - List knownNames = new ArrayList<>(); - knownNames.add("concept"); - knownNames.add("code"); - knownNames.add("status"); - knownNames.add("inactive"); - knownNames.add("effectiveDate"); - knownNames.add("deprecationDate"); - knownNames.add("retirementDate"); - knownNames.add("notSelectable"); - if (cs == null || cs.hasHierarchyMeaning()) { - knownNames.add("parent"); - knownNames.add("child"); - knownNames.add("partOf"); - } - knownNames.add("synonym"); - knownNames.add("comment"); - knownNames.add("itemWeight"); - if (cs != null) { - for (CodeSystemFilterComponent f : cs.getFilter()) { - addName(knownNames, f.getCode()); - } - for (PropertyComponent p : cs.getProperty()) { - addName(knownNames, p.getCode()); - } - } - for (String s : getSystemKnownNames(system)) { - addName(knownNames, s); - } + List knownNames = new ArrayList(); + csChecker.listPropertyNames(knownNames); + boolean pok = false; if (cs == null) { pok = hint(errors, "2024-03-09", IssueType.INVALID, stack, knownNames.contains(property), I18nConstants.VALUESET_UNKNOWN_FILTER_PROPERTY_NO_CS, property, system, CommaSeparatedStringBuilder.join(",", knownNames)); @@ -436,10 +436,10 @@ public class ValueSetValidator extends BaseValidator { pok = warning(errors, "2024-03-09", IssueType.INVALID, stack, knownNames.contains(property), I18nConstants.VALUESET_UNKNOWN_FILTER_PROPERTY, property, system, CommaSeparatedStringBuilder.join(",", knownNames)); } if (pok) { - PropertyValidationRules rules = rulesForFilter(system, cs, property); + PropertyValidationRules rules = csChecker.rulesForFilter(property, EnumSet.noneOf(PropertyOperation.class)); if (rules != null) { if (!rules.getOps().isEmpty()) { - ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, opInSet(op, rules.getOps()), I18nConstants.VALUESET_BAD_FILTER_OP, op, property, CommaSeparatedStringBuilder.join(",", rules.getOps())) && ok; + ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, opInSet(op, rules.getOps()), I18nConstants.VALUESET_BAD_FILTER_OP, op, property, CommaSeparatedStringBuilder.join(",", rules.getOps()), system) && ok; } if ("exists".equals(op)) { @@ -505,9 +505,9 @@ public class ValueSetValidator extends BaseValidator { if (rules.getCodeValidation() == CodeValidationRule.Error || rules.getCodeValidation() == CodeValidationRule.Warning) { ValidationResult vr = context.validateCode(baseOptions, system, version, value, null); if (rules.getCodeValidation() == CodeValidationRule.Error) { - ok = rule(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE, property, value, system, vr.getMessage()) && ok; + ok = rule(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), vr.isOk(), rules.isChange() ? I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE_CHANGE : I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE, property, value, system, vr.getMessage()) && ok; } else { - warning(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE, property, value, system, vr.getMessage()); + warning(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), vr.isOk(), rules.isChange() ? I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE_CHANGE : I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE, property, value, system, vr.getMessage()); } } break; @@ -538,15 +538,6 @@ public class ValueSetValidator extends BaseValidator { ok = rule(errors, "2024-03-09", IssueType.INVALID, stack, vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_CODED_INVALID, property, value, vr.getMessage()) && ok; } break; - case LoincRelationship: - // see https://chat.fhir.org/#narrow/stream/179202-terminology/topic/LOINC.20properties.20in.20filters - // for now, the value can be a LOINC code, or a string value - if (value.matches("(L[A|L|P])?(\\d)+\\-\\d")) { - ValidationResult vr = context.validateCode(baseOptions, system, version, value, null); - ok = rule(errors, "2024-03-09", IssueType.INVALID, stack.getLiteralPath(), vr.isOk(), I18nConstants.VALUESET_BAD_FILTER_VALUE_VALID_CODE_LOINC, property, value, system, vr.getMessage()) && ok; - } else { - // nothing? - } default: break; } @@ -554,293 +545,5 @@ public class ValueSetValidator extends BaseValidator { return ok; } - private PropertyValidationRules rulesForFilter(String system, CodeSystem cs, String property) { - var ops = EnumSet.noneOf(PropertyOperation.class); - - if (cs != null) { - - for (CodeSystemFilterComponent f : cs.getFilter()) { - if (property.equals(f.getCode())) { - for (Enumeration op : f.getOperator()) { - ops.add(toOp(op)); - } - } - } - - for (PropertyComponent p : cs.getProperty()) { - if (property.equals(p.getCode())) { - if (p.getType() != null) { - switch (p.getType()) { - case BOOLEAN: return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops); - case CODE: - // the definitions say " a code that identifies a concept defined in the code system" -> ValidCode. - // but many people have ignored that and defined a property as 'code' because it's from a list of values that are otherwise undefined - boolean external = !forPublication || cs.getWebPath() == null || Utilities.isAbsoluteUrl(cs.getWebPath()); - return new PropertyValidationRules(PropertyFilterType.Code, external ? CodeValidationRule.Warning : CodeValidationRule.Error, ops); // valid code... the definitions say that, but people were missing that in the pastm - case CODING: return new PropertyValidationRules(PropertyFilterType.Coding, null, ops); - case DATETIME: return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops); - case DECIMAL: return new PropertyValidationRules(PropertyFilterType.Decimal, null, ops); - case INTEGER: return new PropertyValidationRules(PropertyFilterType.Integer, null, ops); - case STRING: return null; - } - } - } - } - } - - switch (property) { - case "concept" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In, PropertyOperation.IsA, PropertyOperation.DescendentOf, PropertyOperation.DescendentLeaf, PropertyOperation.IsNotA, PropertyOperation.NotIn)); - case "code" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx)); - case "status" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); - case "inactive" : return new PropertyValidationRules(PropertyFilterType.Boolean,null, ops); - case "effectiveDate" : return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops); - case "deprecationDate" : return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops); - case "retirementDate" : return new PropertyValidationRules(PropertyFilterType.DateTime, null, ops); - case "notSelectable" : return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops); - case "parent" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, ops); - case "child" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, ops); - case "partOf" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, ops); - case "synonym" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); // ? none? - case "comment" : return null; - case "itemWeight" : return new PropertyValidationRules(PropertyFilterType.Decimal, null, ops); - } - switch (system) { - case "http://loinc.org" : - return getLOINCPropertyDetails(property, ops); - case "http://snomed.info/sct": - switch (property) { - case "constraint": return null; // for now - case "expressions": return new PropertyValidationRules(PropertyFilterType.Boolean, null, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In)); - default: - return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.In)); - } - case "http://www.nlm.nih.gov/research/umls/rxnorm" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); - case "http://unitsofmeasure.org" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); - case "http://www.ama-assn.org/go/cpt" : - switch (property) { - case "modifier": return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops); - case "kind" : return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, ops); // for now - case "modified": return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops); - case "code" : return null; - case "telemedicine": return new PropertyValidationRules(PropertyFilterType.Boolean, null, ops); - case "orthopox" : return new PropertyValidationRules(PropertyFilterType.Boolean,null, ops); - } - } - if (ops != null) { - return new PropertyValidationRules(null, null, ops); - } else { - return null; - } - - } - - private PropertyValidationRules getLOINCPropertyDetails(String property, EnumSet ops) { - if (Utilities.existsInList(property, - "parent", - "child", - "answers-for", - "TIME MODIFIER", - "TIME_ASPCT", - "SYSTEM", - "SUPER SYSTEM", - "SUFFIX", - "SCALE", - "SCALE_TYP", - "Rad.View.View Type", - "Rad.View.Aggregation", - "Rad.Timing", - "Rad.Subject", - "Rad.Reason for Exam", - "Rad.Pharmaceutical.Substance Given", - "Rad.Pharmaceutical.Route", - "Rad.Modality.Modality Type", - "Rad.Modality.Modality Subtype", - "Rad.Maneuver.Maneuver Type", - "Rad.Guidance for.Presence", - "Rad.Guidance for.Object", - "Rad.Guidance for.Approach", - "Rad.Guidance for.Action", - "Rad.Anatomic Location.Region Imaged", - "Rad.Anatomic Location.Laterality.Presence", - "Rad.Anatomic Location.Laterality", - "Rad.Anatomic Location.Imaging Focus", - "PROPERTY", - "METHOD", - "GENE", - "Document.TypeOfService", - "Document.SubjectMatterDomain", - "Document.Setting", - "Document.Role", - "Document.Kind", - "DIVISORS", - "COUNT", - "COMPONENT", - "CLASS", - "CHALLENGE", - "AnswerList", - "answer-list", - "Answer", - "ADJUSTMENT")) { - return new PropertyValidationRules(PropertyFilterType.LoincRelationship, CodeValidationRule.Error, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)); - } - - if (Utilities.existsInList(property, - "UNITSREQUIRED", - "PanelType", - "ORDER_OBS", - "EXAMPLE_UNITS", - "EXAMPLE_UCUM_UNITS", - "Copyright", - "CLASSTYPE", - "CLASS", - "AskAtOrderEntry")) { - return new PropertyValidationRules(PropertyFilterType.String, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)); - } - - if (Utilities.existsInList(property, - "STATUS")) { - return new PropertyValidationRules(PropertyFilterType.CodeList, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)) - .setCodes("ACTIVE", "DEPRECATED", "DISCOURAGED", "DocumentOntology", "EXAMPLE", "NORMATIVE", "NotStated", "PREFERRED", "Primary", "Radiology", "TRIAL"); - } - - if (Utilities.existsInList(property, - "LIST", - "ancestor")) { - return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)); - } - - if (Utilities.existsInList(property, - "copyright")) { - return new PropertyValidationRules(PropertyFilterType.CodeList, CodeValidationRule.None, addToOps(ops, PropertyOperation.Equals, PropertyOperation.RegEx, PropertyOperation.In, PropertyOperation.NotIn)).setCodes("LOINC", "3rdParty"); - } - - if (Utilities.existsInList(property, - "concept")) { - return new PropertyValidationRules(PropertyFilterType.Code, CodeValidationRule.None, addToOps(ops, PropertyOperation.IsA)); - } - return null; - } - - - private EnumSet addToOps(EnumSet set, PropertyOperation... ops) { - for (PropertyOperation op : ops) { - set.add(op); - } - return set; - } - - private PropertyOperation toOp(Enumeration op) { - switch (op.getValue()) { - case CHILDOF: return PropertyOperation.ChildOf; - case DESCENDENTLEAF: return PropertyOperation.DescendentLeaf; - case DESCENDENTOF: return PropertyOperation.DescendentOf; - case EQUAL: return PropertyOperation.Equals; - case EXISTS: return PropertyOperation.Exists; - case GENERALIZES: return PropertyOperation.Generalizes; - case IN: return PropertyOperation.In; - case ISA: return PropertyOperation.IsA; - case ISNOTA: return PropertyOperation.IsNotA; - case NOTIN: return PropertyOperation.NotIn; - case REGEX: return PropertyOperation.RegEx; - default: return null; - } - } - private void addName(List knownNames, String code) { - if (code != null && !knownNames.contains(code)) { - knownNames.add(code); - } - } - - private String[] getSystemKnownNames(String system) { - switch (system) { - case "http://loinc.org" : return new String[] { - // = and regex - - "parent", - "child", - "answers-for", - "TIME MODIFIER", - "TIME_ASPCT", - "SYSTEM", - "SUPER SYSTEM", - "SUFFIX", - "SCALE", - "SCALE_TYP", - "Rad.View.View Type", - "Rad.View.Aggregation", - "Rad.Timing", - "Rad.Subject", - "Rad.Reason for Exam", - "Rad.Pharmaceutical.Substance Given", - "Rad.Pharmaceutical.Route", - "Rad.Modality.Modality Type", - "Rad.Modality.Modality Subtype", - "Rad.Maneuver.Maneuver Type", - "Rad.Guidance for.Presence", - "Rad.Guidance for.Object", - "Rad.Guidance for.Approach", - "Rad.Guidance for.Action", - "Rad.Anatomic Location.Region Imaged", - "Rad.Anatomic Location.Laterality.Presence", - "Rad.Anatomic Location.Laterality", - "Rad.Anatomic Location.Imaging Focus", - "PROPERTY", - "METHOD", - "GENE", - "Document.TypeOfService", - "Document.SubjectMatterDomain", - "Document.Setting", - "Document.Role", - "Document.Kind", - "DIVISORS", - "COUNT", - "COMPONENT", - "CLASS", - "CHALLENGE", - "AnswerList", - "answer-list", - "Answer", - "ADJUSTMENT", - "UNITSREQUIRED", - "PanelType", - "ORDER_OBS", - "EXAMPLE_UNITS", - "EXAMPLE_UCUM_UNITS", - "Copyright", - "CLASSTYPE", - "CLASS", - "AskAtOrderEntry", - "ancestor", - - // just equals - "STATUS", - "LIST", - "copyright", - -// == is-a on: - "concept"}; - - case "http://snomed.info/sct": return new String[] { "constraint", "expressions", "410662002", "42752001", "47429007", "116676008", "116686009", "118168003", "118169006", "118170007", "118171006", "127489000", "131195008", - "246075003", "246090004", "246093002", "246112005", "246454002", "246456000", "246501002", "246513007", "246514001", "255234002", "260507000", - "260686004", "260870009", "263502005", "272741003", "288556008", "363589002", "363698007", "363699004", "363700003", "363701004", "363702006", - "363703001", "363704007", "363705008", "363709002", "363710007", "363713009", "363714003", "370129005", "370130000", "370131001", "370132008", - "370133003", "370134009", "370135005", "371881003", "405813007", "405814001", "405815000", "405816004", "408729009", "408730004", "408731000", - "408732007", "410675002", "411116001", "418775008", "419066007", "424226004", "424244007", "424361007", "424876005", "425391005", "609096000", - "704319004", "704320005", "704321009", "704322002", "704323007", "704324001", "704325000", "704326004", "704327008", "704346009", "704347000", - "704647008", "718497002", "719715003", "719722006", "726542003", "726633004", "732943007", "732945000", "732947008", "733722007", "733725009", - "733928003", "733930001", "733931002", "733932009", "733933004", "734136001", "734137005", "736472000", "736473005", "736474004", "736475003", - "736476002", "736518005", "738774007", "762705008", "762706009", "762949000", "762951001", "763032000", "766939001", "774081006", "774158006", - "774159003", "774160008", "774163005", "827081001", "836358009", "840560000", "860779006", "860781008", "1003703000", "1003735000", "1142135004", - "1142136003", "1142137007", "1142138002", "1142139005", "1142140007", "1142141006", "1142142004", "1142143009", "1148793005", "1148965004", - "1148967007", "1148968002", "1148969005", "1149366004", "1149367008", "1230370004", "320091000221107" }; - // list from http://tx.fhir.org/r4/ValueSet/$expand?url=http://snomed.info/sct?fhir_vs=isa/410662002 - - case "http://www.nlm.nih.gov/research/umls/rxnorm" : return new String[] { "STY", "SAB", "TTY", "SY", "SIB", "RN", "PAR", "CHD", "RB", "RO", "IN", "PIN", "MIN", "BN", "SCD", "SBD", "GPCK", "BPCK", "SCDC", "SCDF", "SCDFP", "SCDG", "SCDGP", "SBDC", "SBDF", "SBDFP", "SBDG", "DF", "DFG" }; - case "http://unitsofmeasure.org" : return new String[] { "property", "canonical" }; - case "http://www.ama-assn.org/go/cpt" : return new String[] { "modifier", "kind", "modified", "code", "telemedicine", "orthopox" }; - case "urn:ietf:bcp:47" : return new String[] {"language", "region", "script", "variant", "extension", "ext-lang", "private-use" }; - default: return new String[] { }; - } - } } diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/loinc.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/loinc.cache index d0bdbdd3d..1f48dd94d 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/loinc.cache +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/4.0.1/loinc.cache @@ -7399,7 +7399,6 @@ v: { "error" : "Unknown code 'test' in the CodeSystem 'http://loinc.org' version '2.77'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -7423,3 +7422,67 @@ v: { } ------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://loinc.org", + "code" : "100066-0" +}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": { + "resourceType" : "Parameters", + "parameter" : [{ + "name" : "profile-url", + "valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891" + }] +}}#### +v: { + "display" : "Specular microscopy panel", + "code" : "100066-0", + "system" : "http://loinc.org", + "version" : "2.77", + "server" : "http://tx-dev.fhir.org/r4", + "unknown-systems" : "", + "issues" : { + "resourceType" : "OperationOutcome" +} + +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://loinc.org", + "code" : "100066-0" +}, "url": "http://hl7.org/fhir/test/StructureDefinition/additional-bindings-vs1--0", "version": "1.0.0", "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": { + "resourceType" : "Parameters", + "parameter" : [{ + "name" : "profile-url", + "valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891" + }] +}}#### +v: { + "display" : "Specular microscopy panel", + "code" : "100066-0", + "severity" : "error", + "error" : "The provided code 'http://loinc.org#100066-0' was not found in the value set 'http://hl7.org/fhir/test/StructureDefinition/additional-bindings-vs1--0|1.0.0'", + "class" : "UNKNOWN", + "server" : "http://tx-dev.fhir.org/r4", + "unknown-systems" : "", + "issues" : { + "resourceType" : "OperationOutcome", + "issue" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server", + "valueUrl" : "http://tx-dev.fhir.org/r4" + }], + "severity" : "error", + "code" : "code-invalid", + "details" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", + "code" : "not-in-vs" + }], + "text" : "The provided code 'http://loinc.org#100066-0' was not found in the value set 'http://hl7.org/fhir/test/StructureDefinition/additional-bindings-vs1--0|1.0.0'" + }, + "location" : ["Coding.code"], + "expression" : ["Coding.code"] + }] +} + +} +------------------------------------------------------------------------------------- diff --git a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/loinc.cache b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/loinc.cache index 614b640cb..954ed06a8 100644 --- a/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/loinc.cache +++ b/org.hl7.fhir.validation/src/test/resources/txCache/org.hl7.fhir.validation/5.0.0/loinc.cache @@ -1026,7 +1026,6 @@ v: { "error" : "Unknown code 'LP43571-6' in the CodeSystem 'http://loinc.org' version '2.77'", "class" : "UNKNOWN", "server" : "http://tx-dev.fhir.org/r4", - "unknown-systems" : "", "issues" : { "resourceType" : "OperationOutcome", "issue" : [{ @@ -1050,3 +1049,67 @@ v: { } ------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://loinc.org", + "code" : "100066-0" +}, "valueSet" :null, "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": { + "resourceType" : "Parameters", + "parameter" : [{ + "name" : "profile-url", + "valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891" + }] +}}#### +v: { + "display" : "Specular microscopy panel", + "code" : "100066-0", + "system" : "http://loinc.org", + "version" : "2.77", + "server" : "http://tx-dev.fhir.org/r4", + "unknown-systems" : "", + "issues" : { + "resourceType" : "OperationOutcome" +} + +} +------------------------------------------------------------------------------------- +{"code" : { + "system" : "http://loinc.org", + "code" : "100066-0" +}, "url": "http://hl7.org/fhir/test/StructureDefinition/additional-bindings-vs1--0", "version": "1.0.0", "langs":"", "useServer":"true", "useClient":"false", "guessSystem":"false", "activeOnly":"false", "membershipOnly":"false", "displayWarningMode":"false", "versionFlexible":"false", "profile": { + "resourceType" : "Parameters", + "parameter" : [{ + "name" : "profile-url", + "valueString" : "http://hl7.org/fhir/ExpansionProfile/dc8fd4bc-091a-424a-8a3b-6198ef146891" + }] +}}#### +v: { + "display" : "Specular microscopy panel", + "code" : "100066-0", + "severity" : "error", + "error" : "The provided code 'http://loinc.org#100066-0' was not found in the value set 'http://hl7.org/fhir/test/StructureDefinition/additional-bindings-vs1--0|1.0.0'", + "class" : "UNKNOWN", + "server" : "http://tx-dev.fhir.org/r4", + "unknown-systems" : "", + "issues" : { + "resourceType" : "OperationOutcome", + "issue" : [{ + "extension" : [{ + "url" : "http://hl7.org/fhir/StructureDefinition/operationoutcome-issue-server", + "valueUrl" : "http://tx-dev.fhir.org/r4" + }], + "severity" : "error", + "code" : "code-invalid", + "details" : { + "coding" : [{ + "system" : "http://hl7.org/fhir/tools/CodeSystem/tx-issue-type", + "code" : "not-in-vs" + }], + "text" : "The provided code 'http://loinc.org#100066-0' was not found in the value set 'http://hl7.org/fhir/test/StructureDefinition/additional-bindings-vs1--0|1.0.0'" + }, + "location" : ["Coding.code"], + "expression" : ["Coding.code"] + }] +} + +} +------------------------------------------------------------------------------------- From b3a969e3ce9600fa26666c5fe9a515dd1b9edd38 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sat, 18 May 2024 07:36:51 -0500 Subject: [PATCH 11/14] Don't accidently hit other terminology servers for code systems handled natively by tx.fhir.org --- .../client/TerminologyClientManager.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java index 276bea020..5f060ee7d 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/terminologies/client/TerminologyClientManager.java @@ -185,6 +185,19 @@ public class TerminologyClientManager { } } + for (String sys : systems) { + String uri = sys.contains("|") ? sys.substring(0, sys.indexOf("|")) : sys; + // this list is the list of code systems that have special handling on tx.fhir.org, and might not be resolved above. + // we don't want them to go to secondary servers (e.g. VSAC) by accident (they might go deliberately above) + if (Utilities.existsInList(uri, "http://unitsofmeasure.org", "http://loinc.org", "http://snomed.info/sct", + "http://www.nlm.nih.gov/research/umls/rxnorm", "http://hl7.org/fhir/sid/cvx", "urn:ietf:bcp:13", "urn:ietf:bcp:47", + "urn:ietf:rfc:3986", "http://www.ama-assn.org/go/cpt", "urn:oid:1.2.36.1.2001.1005.17", "urn:iso:std:iso:3166", + "http://varnomen.hgvs.org", "http://unstats.un.org/unsd/methods/m49/m49.htm", "urn:iso:std:iso:4217", + "http://hl7.org/fhir/sid/ndc", "http://fhir.ohdsi.org/CodeSystem/concepts", "http://fdasis.nlm.nih.gov", "https://www.usps.com/")) { + return serverList.get(0); + } + } + // no agreement? Then what we do depends if (vs != null) { if (vs.hasUserData("External.Link")) { From 5662ec131f2d680da49fd4de113bb1099974bd9e Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sun, 19 May 2024 09:36:05 -0500 Subject: [PATCH 12/14] WHO i18n work (#1592) --- .../StructureDefinitionComparer.java | 23 +-- .../renderers/AdditionalBindingsRenderer.java | 4 +- .../hl7/fhir/r5/renderers/BinaryRenderer.java | 6 +- .../fhir/r5/renderers/CodeSystemRenderer.java | 6 +- .../fhir/r5/renderers/ConceptMapRenderer.java | 2 +- .../hl7/fhir/r5/renderers/DataRenderer.java | 97 +++++------ .../renderers/DiagnosticReportRenderer.java | 6 +- .../r5/renderers/ExampleScenarioRenderer.java | 4 +- .../r5/renderers/ProfileDrivenRenderer.java | 4 +- .../QuestionnaireResponseRenderer.java | 66 ++++---- .../org/hl7/fhir/r5/renderers/Renderer.java | 29 ++-- .../fhir/r5/renderers/ResourceRenderer.java | 20 +-- .../r5/renderers/SearchParameterRenderer.java | 4 +- .../StructureDefinitionRenderer.java | 118 ++++++------- .../r5/renderers/StructureMapRenderer.java | 5 +- .../r5/renderers/TerminologyRenderer.java | 2 +- .../fhir/r5/renderers/TestPlanRenderer.java | 2 +- .../fhir/r5/renderers/ValueSetRenderer.java | 51 +++--- .../utilities/i18n/RenderingI18nContext.java | 157 +++++++++++++++++- .../xhtml/HierarchicalTableGenerator.java | 32 ++-- .../resources/rendering-phrases.properties | 156 +++++++++++++++++ 21 files changed, 543 insertions(+), 251 deletions(-) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructureDefinitionComparer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructureDefinitionComparer.java index 0727512fd..3f562a249 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructureDefinitionComparer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/comparison/StructureDefinitionComparer.java @@ -42,6 +42,7 @@ import org.hl7.fhir.r5.renderers.utils.RenderingContext.ResourceRendererMode; import org.hl7.fhir.r5.utils.DefinitionNavigator; import org.hl7.fhir.utilities.CommaSeparatedStringBuilder; import org.hl7.fhir.utilities.Utilities; +import org.hl7.fhir.utilities.i18n.I18nConstants; import org.hl7.fhir.utilities.i18n.RenderingI18nContext; import org.hl7.fhir.utilities.validation.ValidationMessage; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; @@ -1289,32 +1290,32 @@ public class StructureDefinitionComparer extends CanonicalResourceComparer imple boolean ext = false; if (tail(path).equals("extension")) { if (elementIsComplex(combined)) - row.setIcon("icon_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); + row.setIcon("icon_extension_complex.png", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_COMPLEX)); else - row.setIcon("icon_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); + row.setIcon("icon_extension_simple.png", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_SIMPLE)); ext = true; } else if (tail(path).equals("modifierExtension")) { if (elementIsComplex(combined)) - row.setIcon("icon_modifier_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); + row.setIcon("icon_modifier_extension_complex.png", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_COMPLEX)); else - row.setIcon("icon_modifier_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); + row.setIcon("icon_modifier_extension_simple.png", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_SIMPLE)); } else if (hasChoice(combined)) { if (allAreReference(combined)) - row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + row.setIcon("icon_reference.png", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_REFERENCE)); else { - row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); + row.setIcon("icon_choice.gif", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_CHOICE)); typesRow = row; } } else if (combined.either().getDef().hasContentReference()) - row.setIcon("icon_reuse.png", HierarchicalTableGenerator.TEXT_ICON_REUSE); + row.setIcon("icon_reuse.png", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_REUSE)); else if (isPrimitive(combined)) - row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); + row.setIcon("icon_primitive.png", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_PRIMITIVE)); else if (hasTarget(combined)) - row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + row.setIcon("icon_reference.png", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_REFERENCE)); else if (isDataType(combined)) - row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); + row.setIcon("icon_datatype.gif", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_DATATYPE)); else - row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); + row.setIcon("icon_resource.png", utilsLeft.getContext().formatMessage(RenderingI18nContext.TEXT_ICON_RESOURCE)); String ref = defPath == null ? null : defPath + combined.either().getDef().getId(); String sName = tail(path); String sn = getSliceName(combined); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/AdditionalBindingsRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/AdditionalBindingsRenderer.java index f906be94f..bbc768ade 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/AdditionalBindingsRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/AdditionalBindingsRenderer.java @@ -296,8 +296,8 @@ public class AdditionalBindingsRenderer { } } if (any) { - String newRepeat = binding.any ? /*!#*/"Any repeats" : /*!#*/"All repeats"; - String oldRepeat = binding.compare!=null && binding.compare.any ? /*!#*/"Any repeats" : /*!#*/"All repeats"; + String newRepeat = binding.any ? context.formatMessage(RenderingContext.ADD_BIND_ANY_REP) : context.formatMessage(RenderingContext.ADD_BIND_ALL_REP); + String oldRepeat = binding.compare!=null && binding.compare.any ? context.formatMessage(RenderingContext.ADD_BIND_ANY_REP) : context.formatMessage(RenderingContext.ADD_BIND_ALL_REP); compareString(tr.td().style("font-size: 11px"), newRepeat, oldRepeat); } if (doco) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BinaryRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BinaryRenderer.java index 9273d3c84..3fb22f19e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BinaryRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/BinaryRenderer.java @@ -71,7 +71,7 @@ public class BinaryRenderer { public void render(XhtmlNode x, String id, String ct, byte[] cnt) throws IOException { filenames.clear(); if (ct == null) { - error(x, /*!#*/"No Content Type"); + error(x,"No Content Type"); } else if (ct.startsWith("image/")) { image(x, id, ct, cnt); } else if (isXml(ct)) { @@ -83,7 +83,7 @@ public class BinaryRenderer { } else if (isText(ct)) { text(x, cnt); } else { - error(x, /*!#*/"The Content Type '"+ct+"' is not rendered in this context"); + error(x, "The Content Type '"+ct+"' is not rendered in this context"); } } @@ -101,7 +101,7 @@ public class BinaryRenderer { } if (ext == null) { - error(x, /*!#*/"The Image Type '"+ct+"' is not rendered in this context"); + error(x, "The Image Type '"+ct+"' is not rendered in this context"); } else { String fn = "Binary-Native-"+id+ext; TextFile.bytesToFile(cnt, Utilities.path(folder, fn)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java index 91b8e5a72..3264b8334 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/CodeSystemRenderer.java @@ -506,7 +506,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { hasExtensions = true; if (ToolingExtensions.hasExtension(c, ToolingExtensions.EXT_REPLACED_BY)) { Coding cc = (Coding) ToolingExtensions.getExtension(c, ToolingExtensions.EXT_REPLACED_BY).getValue(); - td.tx(" "+/*!#*/"(replaced by "); + td.tx(" "+ context.formatMessage(RenderingContext.CODE_SYS_REPLACED_BY) + " "); String url = getCodingReference(cc, system); if (url != null) { td.ah(url).addText(cc.getCode()); @@ -636,9 +636,9 @@ public class CodeSystemRenderer extends TerminologyRenderer { } if (context.isCopyButton()) { td = tr.td(); - clipboard(td, "icon_clipboard_x.png", /*!#*/"XML", "\n"+(cs.getVersionNeeded() ? "\n" : "")+"\n\n"); + clipboard(td, "icon_clipboard_x.png", "XML", "\n"+(cs.getVersionNeeded() ? "\n" : "")+"\n\n"); td.nbsp(); - clipboard(td, "icon_clipboard_j.png", /*!#*/"JSON", "\"system\" : \""+Utilities.escapeXml(cs.getUrl())+"\",\n"+(cs.getVersionNeeded() ? "\"version\" : \""+Utilities.escapeXml(cs.getVersion())+"\",\n" : "")+"\"code\" : \""+Utilities.escapeXml(c.getCode())+"\",\n\"display\" : \""+Utilities.escapeXml(c.getDisplay())+"\"\n"); + clipboard(td, "icon_clipboard_j.png", "JSON", "\"system\" : \""+Utilities.escapeXml(cs.getUrl())+"\",\n"+(cs.getVersionNeeded() ? "\"version\" : \""+Utilities.escapeXml(cs.getVersion())+"\",\n" : "")+"\"code\" : \""+Utilities.escapeXml(c.getCode())+"\",\n\"display\" : \""+Utilities.escapeXml(c.getDisplay())+"\"\n"); } return hasExtensions; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ConceptMapRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ConceptMapRenderer.java index 010f4262b..5b304246e 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ConceptMapRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ConceptMapRenderer.java @@ -783,7 +783,7 @@ public class ConceptMapRenderer extends TerminologyRenderer { private static void renderLinks(XhtmlNode td, List collateral) { if (collateral.size() > 0) { - td.tx(/*!#*/"Links:"); + td.tx( "Links:"); td.tx(" "); boolean first = true; for (CollateralDefinition c : collateral) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java index bdddf9b10..31965a709 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DataRenderer.java @@ -219,20 +219,20 @@ public class DataRenderer extends Renderer implements CodeResolver { // -- 3. General Purpose Terminology Support ----------------------------------------- - private static String month(String m) { + private static String snMonth(String m) { switch (m) { - case "1" : return /*!#*/"Jan"; - case "2" : return /*!#*/"Feb"; - case "3" : return /*!#*/"Mar"; - case "4" : return/*!#*/ "Apr"; - case "5" : return /*!#*/"May"; - case "6" : return /*!#*/"Jun"; - case "7" : return /*!#*/"Jul"; - case "8" : return /*!#*/"Aug"; - case "9" : return /*!#*/"Sep"; - case "10" : return /*!#*/"Oct"; - case "11" : return /*!#*/"Nov"; - case "12" : return /*!#*/"Dec"; + case "1" : return "Jan"; + case "2" : return "Feb"; + case "3" : return "Mar"; + case "4" : return "Apr"; + case "5" : return "May"; + case "6" : return "Jun"; + case "7" : return "Jul"; + case "8" : return "Aug"; + case "9" : return "Sep"; + case "10" : return "Oct"; + case "11" : return "Nov"; + case "12" : return "Dec"; default: return null; } } @@ -247,21 +247,21 @@ public class DataRenderer extends Renderer implements CodeResolver { ed = p[p.length-3]; String y = p[p.length-3].substring(4, 8); String m = p[p.length-3].substring(2, 4); - dt = " rel. "+month(m)+" "+y; + dt = " rel. "+snMonth(m)+" "+y; } else { ed = p[p.length-1]; } switch (ed) { - case "900000000000207008": return /*!#*/"Intl"+dt; - case "731000124108": return /*!#*/"US"+dt; - case "32506021000036107": return /*!#*/"AU"+dt; - case "449081005": return /*!#*/"ES"+dt; - case "554471000005108": return /*!#*/"DK"+dt; - case "11000146104": return /*!#*/"NL"+dt; - case "45991000052106": return /*!#*/"SE"+dt; - case "999000041000000102": return /*!#*/"UK"+dt; - case "20611000087101": return /*!#*/"CA"+dt; - case "11000172109": return /*!#*/"BE"+dt; + case "900000000000207008": return "Intl"+dt; + case "731000124108": return "US"+dt; + case "32506021000036107": return "AU"+dt; + case "449081005": return "ES"+dt; + case "554471000005108": return "DK"+dt; + case "11000146104": return "NL"+dt; + case "45991000052106": return "SE"+dt; + case "999000041000000102": return "UK"+dt; + case "20611000087101": return "CA"+dt; + case "11000172109": return "BE"+dt; default: return "??"+dt; } } else { @@ -269,25 +269,6 @@ public class DataRenderer extends Renderer implements CodeResolver { } } -// public static String describeSystem(String system) { -// if (system == null) -// return /*!#*/ "[not stated]"; -// if (system.equals("http://loinc.org")) -// return /*!#*/ "LOINC"; -// if (system.startsWith("http://snomed.info")) -// return /*!#*/ "SNOMED CT"; -// if (system.equals("http://www.nlm.nih.gov/research/umls/rxnorm")) -// return /*!#*/ "RxNorm"; -// if (system.equals("http://hl7.org/fhir/sid/icd-9")) -// return /*!#*/ "ICD-9"; -// if (system.equals("http://dicom.nema.org/resources/ontology/DCM")) -// return /*!#*/ "DICOM"; -// if (system.equals("http://unitsofmeasure.org")) -// return /*!#*/ "UCUM"; -// -// return system; -// } - public String displaySystem(String system) { if (system == null) return (context.formatMessage(RenderingContext.DATA_REND_NOT_STAT)); @@ -357,7 +338,7 @@ public class DataRenderer extends Renderer implements CodeResolver { protected String describeLang(String lang) { // special cases: if ("fr-CA".equals(lang)) { - return /*!#*/"French (Canadian)"; // this one was omitted from the value set + return "French (Canadian)"; // this one was omitted from the value set } ValueSet v = getContext().getWorker().findTxResource(ValueSet.class, "http://hl7.org/fhir/ValueSet/languages"); if (v != null) { @@ -703,7 +684,7 @@ public class DataRenderer extends Renderer implements CodeResolver { if (base instanceof DataType) { render(x, (DataType) base); } else { - x.tx(/*!#*/"to do: "+base.fhirType()); + x.tx(context.formatMessage(RenderingContext.DATA_REND_TO_DO, base.fhirType())); } } @@ -763,7 +744,7 @@ public class DataRenderer extends Renderer implements CodeResolver { addMarkdown(x, context.getTranslated((MarkdownType) type)); } else if (type instanceof Base64BinaryType) { Base64BinaryType b64 = (Base64BinaryType) type; - x.tx(/*!#*/"(base64 data - "+(b64.getValue() == null ? "0" : b64.getValue().length)+" bytes)"); + x.tx(context.formatMessage(RenderingContext.DATA_REND_BASE64, (b64.getValue() == null ? "0" : b64.getValue().length))); } else if (type.isPrimitive()) { x.tx(context.getTranslated((PrimitiveType) type)); } else { @@ -1057,7 +1038,7 @@ public class DataRenderer extends Renderer implements CodeResolver { systemName = cs != null ? crPresent(cs) : displaySystem(c.getSystem()); link = getLinkForCode(c.getSystem(), c.getVersion(), c.getCode()); - hint = systemName+": "+display+(c.hasVersion() ? " "+/*!#*/"(version = "+c.getVersion()+")" : ""); + hint = systemName+": "+display+(c.hasVersion() ? " "+ context.formatMessage(RenderingContext.DATA_REND_VERSION, c.getVersion(), ")") : ""); return new CodeResolution(systemName, systemLink, link, display, hint); } @@ -1092,7 +1073,7 @@ public class DataRenderer extends Renderer implements CodeResolver { x.tx(s); } if (c.hasVersion()) { - x.tx(" "+/*!#*/"(version = "+c.getVersion()+")"); + x.tx(" "+context.formatMessage(RenderingContext.DATA_REND_VERSION, c.getVersion(), ")")); } } @@ -1107,7 +1088,7 @@ public class DataRenderer extends Renderer implements CodeResolver { s = c.getCode(); if (showCodeDetails) { - x.addText(s+" "+/*!#*/"(Details: "+displaySystem(c.getSystem())+" code "+c.getCode()+" = '"+lookupCode(c.getSystem(), c.getVersion(), c.getCode())+"', stated as '"+c.getDisplay()+"')"); + x.addText(s+" "+context.formatMessage(RenderingContext.DATA_REND_DETAILS_STATED, displaySystem(c.getSystem()), c.getCode(), " = '", lookupCode(c.getSystem(), c.getVersion(), c.getCode()), c.getDisplay(), "')")); } else x.span(null, "{"+c.getSystem()+" "+c.getCode()+"}").addText(s); } @@ -1227,7 +1208,7 @@ public class DataRenderer extends Renderer implements CodeResolver { } } - x.span(null, /*!#*/"Codes: "+b.toString()).addText(s); + x.span(null, context.formatMessage(RenderingContext.DATA_REND_CODES) +b.toString()).addText(s); } } @@ -1291,7 +1272,7 @@ public class DataRenderer extends Renderer implements CodeResolver { } else { switch (ii.getSystem()) { case "urn:oid:2.51.1.3": - x.ah("https://www.gs1.org/standards/id-keys/gln", /*!#*/"Global Location Number").tx("GLN"); + x.ah("https://www.gs1.org/standards/id-keys/gln", context.formatMessage(RenderingContext.DATA_REND_GLN)).tx("GLN"); break; default: x.code(ii.getSystem()); @@ -1557,8 +1538,8 @@ public class DataRenderer extends Renderer implements CodeResolver { if (system == null) return ""; switch (system) { - case PHONE: return /*!#*/"ph: "; - case FAX: return /*!#*/"fax: "; + case PHONE: return "ph: "; + case FAX: return "fax: "; default: return ""; } @@ -1596,7 +1577,7 @@ public class DataRenderer extends Renderer implements CodeResolver { x.tx("(unit "+q.getCode()+" from "+q.getSystem()+")"); } if (showCodeDetails && q.hasCode()) { - x.span("background: LightGoldenRodYellow", null).tx(" "+/*!#*/"(Details: "+displaySystem(q.getSystem())+" code "+q.getCode()+" = '"+lookupCode(q.getSystem(), null, q.getCode())+"')"); + x.span("background: LightGoldenRodYellow", null).tx(" "+ (context.formatMessage(RenderingContext.DATA_REND_DETAILS, displaySystem(q.getSystem()))) +q.getCode()+" = '"+lookupCode(q.getSystem(), null, q.getCode())+"')"); } } @@ -1640,13 +1621,13 @@ public class DataRenderer extends Renderer implements CodeResolver { public String displayPeriod(Period p) { String s = !p.hasStart() ? "(?)" : displayDateTime(p.getStartElement()); s = s + " --> "; - return s + (!p.hasEnd() ? /*!#*/"(ongoing)" : displayDateTime(p.getEndElement())); + return s + (!p.hasEnd() ? context.formatMessage(RenderingContext.DATA_REND_ONGOING) : displayDateTime(p.getEndElement())); } public void renderPeriod(XhtmlNode x, Period p) { x.addText(!p.hasStart() ? "??" : displayDateTime(p.getStartElement())); x.tx(" --> "); - x.addText(!p.hasEnd() ? /*!#*/"(ongoing)" : displayDateTime(p.getEndElement())); + x.addText(!p.hasEnd() ? context.formatMessage(RenderingContext.DATA_REND_ONGOING) : displayDateTime(p.getEndElement())); } public void renderUsageContext(XhtmlNode x, UsageContext u) throws FHIRFormatError, DefinitionException, IOException { @@ -1856,7 +1837,7 @@ public class DataRenderer extends Renderer implements CodeResolver { st = st + "-"+Integer.toString(rep.getFrequency()); } if (rep.hasPeriod()) { - st = st + " "+/*!#*/"per "+rep.getPeriod().toPlainString(); + st = st + " "+ (context.formatMessage(RenderingContext.DATA_REND_PER))+rep.getPeriod().toPlainString(); if (rep.hasPeriodMax()) st = st + "-"+rep.getPeriodMax().toPlainString(); st = st + " "+displayTimeUnits(rep.getPeriodUnit()); @@ -1961,7 +1942,7 @@ public class DataRenderer extends Renderer implements CodeResolver { XhtmlNode xn; xn = new XhtmlNode(NodeType.Element, "div"); XhtmlNode p = xn.para(); - p.b().tx(/*!#*/"Exception "+function+": "+e.getMessage()); + p.b().tx((context.formatMessage(RenderingContext.DATA_REND_EXCEPTION)) +function+": "+e.getMessage()); p.addComment(getStackTrace(e)); return xn; } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java index 2b19ad073..b95c46e77 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/DiagnosticReportRenderer.java @@ -85,7 +85,7 @@ public class DiagnosticReportRenderer extends ResourceRenderer { pw = getProperty(dr, "perfomer"); if (valued(pw)) { tr = tbl.tr(); - tr.td().tx(Utilities.pluralize(/*!#*/ "Performer", pw.getValues().size())); + tr.td().tx(Utilities.pluralize((context.formatMessage(RenderingContext.DIAG_REP_REND_PER)), pw.getValues().size())); XhtmlNode tdr = tr.td(); for (BaseWrapper v : pw.getValues()) { tdr.tx(" "); @@ -95,7 +95,7 @@ public class DiagnosticReportRenderer extends ResourceRenderer { pw = getProperty(dr, "identifier"); if (valued(pw)) { tr = tbl.tr(); - tr.td().tx(Utilities.pluralize(/*!#*/"Identifier", pw.getValues().size())+":"); + tr.td().tx(Utilities.pluralize((context.formatMessage(RenderingContext.DIAG_REP_REND_IDENTIFIER)), pw.getValues().size())+":"); XhtmlNode tdr = tr.td(); for (BaseWrapper v : pw.getValues()) { tdr.tx(" "); @@ -105,7 +105,7 @@ public class DiagnosticReportRenderer extends ResourceRenderer { pw = getProperty(dr, "request"); if (valued(pw)) { tr = tbl.tr(); - tr.td().tx(Utilities.pluralize(/*!#*/"Request", pw.getValues().size())+":"); + tr.td().tx(Utilities.pluralize((context.formatMessage(RenderingContext.DIAG_REP_REND_REQUEST)), pw.getValues().size())+":"); XhtmlNode tdr = tr.td(); for (BaseWrapper v : pw.getValues()) { tdr.tx(" "); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java index 7ab02111c..a4d881b8b 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ExampleScenarioRenderer.java @@ -119,7 +119,7 @@ public class ExampleScenarioRenderer extends TerminologyRenderer { XhtmlNode n = new XhtmlDocument(); renderCanonical(scen, n, step.getWorkflow()); XhtmlNode ref = n.getChildNodes().get(0); - plantUml += noteOver(scen.getActor(), /*!#*/"Step " + trimPrefix(prefix) + " - See scenario\n" + creolLink(ref.getContent(), ref.getAttribute("href"))); + plantUml += noteOver(scen.getActor(), context.formatMessage(RenderingContext.EXAMPLE_SCEN_STEP_SCEN, trimPrefix(prefix), creolLink((ref.getContent()), ref.getAttribute("href")))); } else if (step.hasProcess()) plantUml += toPlantUml(step.getProcess(), prefix, scen, actorKeys); else { @@ -478,7 +478,7 @@ public class ExampleScenarioRenderer extends TerminologyRenderer { } } if (theVersion==null) - throw new FHIRException(/*!#*/"Unable to find referenced version " + instanceRef.getVersionReference() + " within instance " + instanceRef.getInstanceReference()); + throw new FHIRException("Unable to find referenced version " + instanceRef.getVersionReference() + " within instance " + instanceRef.getInstanceReference()); instanceCell.ah("#i_" + instance.getKey() + "v_"+ theVersion.getKey() , theVersion.getDescription()).tx(theVersion.getTitle()); } else diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java index 25c08620a..0623bda9c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ProfileDrivenRenderer.java @@ -107,7 +107,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { boolean idDone = false; XhtmlNode p = x.para(); if (context.isAddGeneratedNarrativeHeader()) { - p.b().tx(/*!#*/"Generated Narrative: "+r.fhirType()+(context.isContained() ? " #"+r.getId() : "")); + p.b().tx(context.formatMessage(RenderingContext.PROF_DRIV_GEN_NARR, r.fhirType(), (context.isContained() ? " #"+r.getId() : ""))); if (!Utilities.noString(r.getId())) { p.an(r.getId()); p.an("hc"+r.getId()); @@ -133,7 +133,7 @@ public class ProfileDrivenRenderer extends ResourceRenderer { generateByProfile(r, sd, r.root(), sd.getSnapshot().getElement(), ed, context.getProfileUtilities().getChildList(sd, ed), x, r.fhirType(), context.isTechnicalMode(), 0); } } catch (Exception e) { - System.out.println(/*!#*/"Error Generating Narrative for "+r.fhirType()+"/"+r.getId()+": "+e.getMessage()); + System.out.println(context.formatMessage(RenderingContext.PROF_DRIV_ERR_GEN_NARR) +r.fhirType()+"/"+r.getId()+": "+e.getMessage()); e.printStackTrace(); x.para().b().style("color: maroon").tx(context.formatMessage(RenderingContext.PROF_DRIV_EXCP, e.getMessage())+" "); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireResponseRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireResponseRenderer.java index e1c68fcba..8589632ec 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireResponseRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/QuestionnaireResponseRenderer.java @@ -45,7 +45,7 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { // case DEFNS: return renderDefns(x, q); case TREE: return renderTree(x, q); default: - throw new Error(/*!#*/"Unknown QuestionnaireResponse Renderer Mode"); + throw new Error(context.formatMessage(RenderingContext.QUEST_UNKNOWN_MODE)); } } @@ -57,7 +57,7 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { // case DEFNS: return renderDefns(x, q); case TREE: return renderTree(x, qr); default: - throw new Error(/*!#*/"Unknown QuestionnaireResponse Renderer Mode"); + throw new Error(context.formatMessage(RenderingContext.QUEST_UNKNOWN_MODE)); } } @@ -71,10 +71,10 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { model.setDocoImg(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "help16.png")); } model.setDocoRef(context.getLink(KnownLinkType.SPEC)+"formats.html#table"); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), /*!#*/"LinkId", /*!#*/"The linkId for the item", null, 0)); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), /*!#*/"Text", /*!#*/"Text for the item", null, 0)); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), /*!#*/"Definition", /*!#*/"Minimum and Maximum # of times the the itemcan appear in the instance", null, 0)); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), /*!#*/"Answer", /*!#*/"The type of the item", null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatMessage(RenderingContext.QUEST_LINKID), context.formatMessage(RenderingContext.QUEST_LINK), null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatMessage(RenderingContext.QUEST_TEXT), context.formatMessage(RenderingContext.QUEST_TEXTFOR), null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatMessage(RenderingContext.QUEST_DEFINITION), context.formatMessage(RenderingContext.QUEST_TIMES), null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatMessage(RenderingContext.QUEST_ANSWER), context.formatMessage(RenderingContext.QUEST_TYPE_ITEM), null, 0)); boolean hasExt = false; // first we add a root for the questionaire itself @@ -98,10 +98,10 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { model.setDocoImg(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "help16.png")); } model.setDocoRef(context.getLink(KnownLinkType.SPEC)+"formats.html#table"); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), /*!#*/"LinkId", /*!#*/"The linkId for the item", null, 0)); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), /*!#*/"Text", /*!#*/"Text for the item", null, 0)); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), /*!#*/"Definition", /*!#*/"Minimum and Maximum # of times the the itemcan appear in the instance", null, 0)); - model.getTitles().add(gen.new Title(null, model.getDocoRef(), /*!#*/"Answer", /*!#*/"The type of the item", null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatMessage(RenderingContext.QUEST_LINKID), context.formatMessage(RenderingContext.QUEST_LINK), null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatMessage(RenderingContext.QUEST_TEXT), context.formatMessage(RenderingContext.QUEST_TEXTFOR), null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatMessage(RenderingContext.QUEST_DEFINITION), context.formatMessage(RenderingContext.QUEST_TIMES), null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), context.formatMessage(RenderingContext.QUEST_ANSWER), context.formatMessage(RenderingContext.QUEST_TYPE_ITEM), null, 0)); boolean hasExt = false; // first we add a root for the questionaire itself @@ -120,10 +120,10 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { Row r = gen.new Row(); rows.add(r); - r.setIcon("icon_q_root.gif", /*!#*/"QuestionnaireResponseRoot"); + r.setIcon("icon_q_root.gif", context.formatMessage(RenderingContext.QUEST_RESP_ROOT)); r.getCells().add(gen.new Cell(null, null, q.getId(), null, null)); r.getCells().add(gen.new Cell(null, null, "", null, null)); - r.getCells().add(gen.new Cell(null, null, /*!#*/"QuestionnaireResponse", null, null)); + r.getCells().add(gen.new Cell(null, null, context.formatMessage(RenderingContext.QUEST_RESP), null, null)); r.getCells().add(gen.new Cell(null, null, "", null, null)); return r; } @@ -136,18 +136,18 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { String ref = b == null ? null : b.primitiveValue(); Questionnaire q = context.getContext().fetchResource(Questionnaire.class, ref); - r.setIcon("icon_q_root.gif", /*!#*/"QuestionnaireResponseRoot"); + r.setIcon("icon_q_root.gif", context.formatMessage(RenderingContext.QUEST_RESP_ROOT)); r.getCells().add(gen.new Cell(null, null, qr.getId(), null, null)); r.getCells().add(gen.new Cell(null, null, "", null, null)); if (ref == null ) { r.getCells().add(gen.new Cell(null, null, "", null, null)); - r.getCells().add(gen.new Cell(/*!#*/"Questionnaire:", null, /*!#*/"None specified", null, null)); + r.getCells().add(gen.new Cell(context.formatMessage(RenderingContext.QUEST_QUESTION), null, context.formatMessage(RenderingContext.QUEST_NONE_SPEC), null, null)); } else if (q == null || !q.hasWebPath()) { r.getCells().add(gen.new Cell(null, null, "", null, null)); - r.getCells().add(gen.new Cell(/*!#*/"Questionnaire:", null, ref, null, null)); + r.getCells().add(gen.new Cell(context.formatMessage(RenderingContext.QUEST_QUESTION), null, ref, null, null)); } else{ r.getCells().add(gen.new Cell(null, null, "", null, null)); - r.getCells().add(gen.new Cell(/*!#*/"Questionnaire:", q.getWebPath(), q.present(), null, null)); + r.getCells().add(gen.new Cell(context.formatMessage(RenderingContext.QUEST_QUESTION), q.getWebPath(), q.present(), null, null)); } return r; } @@ -168,9 +168,9 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { } } if (hasItem) { - r.setIcon("icon-q-group.png", /*!#*/"Group"); + r.setIcon("icon-q-group.png", context.formatMessage(RenderingContext.QUEST_GROUP)); } else { - r.setIcon("icon-q-string.png", /*!#*/"Item"); + r.setIcon("icon-q-string.png", context.formatMessage(RenderingContext.QUEST_ITEM)); } String linkId = i.has("linkId") ? i.get("linkId").primitiveValue() : "??"; String text = i.has("text") ? i.get("text").primitiveValue() : ""; @@ -235,9 +235,9 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { hasItem = a.hasItem(); } if (hasItem) { - r.setIcon("icon-q-group.png", /*!#*/"Group"); + r.setIcon("icon-q-group.png", context.formatMessage(RenderingContext.QUEST_GROUP)); } else { - r.setIcon("icon-q-string.png", /*!#*/"Item"); + r.setIcon("icon-q-string.png", context.formatMessage(RenderingContext.QUEST_ITEM)); } r.getCells().add(gen.new Cell(null, context.getDefinitionsTarget() == null ? "" : context.getDefinitionsTarget()+"#item."+i.getLinkId(), i.getLinkId(), null, null)); r.getCells().add(gen.new Cell(null, null, i.getText(), null, null)); @@ -327,7 +327,7 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { public boolean renderForm(XhtmlNode x, ResourceWrapper q) throws UnsupportedEncodingException, IOException { boolean hasExt = false; XhtmlNode d = x.div(); - d.tx(/*!#*/"todo"); + d.tx(context.formatMessage(RenderingContext.QUEST_TODO)); // boolean hasPrefix = false; // for (QuestionnaireItemComponent c : q.getItem()) { // hasPrefix = hasPrefix || doesItemHavePrefix(c); @@ -370,7 +370,7 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { // } // p.span(null, "linkId: "+i.getLinkId()).tx(i.getText()); // if (i.getRequired()) { -// p.span("color: red", /*!#*/"Mandatory").tx("*"); +// p.span("color: red", context.formatMessage(RenderingContext.QUEST_MAND)).tx("*"); // } // // XhtmlNode input = null; @@ -605,16 +605,16 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { // } // private boolean renderLinks(XhtmlNode x, QuestionnaireResponse q) { - x.para().tx(/*!#*/"Try this QuestionnaireResponse out:"); + x.para().tx(context.formatMessage(RenderingContext.QUEST_TRY_QUEST)); XhtmlNode ul = x.ul(); - ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getLink(KnownLinkType.SELF), "package.tgz")+"&q="+q.getId()+".json").tx(/*!#*/"NLM Forms Library"); + ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getLink(KnownLinkType.SELF), "package.tgz")+"&q="+q.getId()+".json").tx(context.formatMessage(RenderingContext.QUEST_NLM)); return false; } private boolean renderLinks(XhtmlNode x, ResourceWrapper q) { - x.para().tx(/*!#*/"Try this QuestionnaireResponse out:"); + x.para().tx(context.formatMessage(RenderingContext.QUEST_TRY_QUEST)); XhtmlNode ul = x.ul(); - ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getLink(KnownLinkType.SELF), "package.tgz")+"&q="+q.getId()+".json").tx(/*!#*/"NLM Forms Library"); + ul.li().ah("http://todo.nlm.gov/path?mode=ig&src="+Utilities.pathURL(context.getLink(KnownLinkType.SELF), "package.tgz")+"&q="+q.getId()+".json").tx(context.formatMessage(RenderingContext.QUEST_NLM)); return false; } @@ -711,13 +711,13 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { // } // if (qi.hasAnswerOption()) { // XhtmlNode tr = tbl.tr(); -// tr.td().tx(/*!#*/"Allowed Answers"); +// tr.td().tx(context.formatMessage(RenderingContext.QUEST_ALLOWED)); // XhtmlNode ul = tr.td().ul(); // for (QuestionnaireItemAnswerOptionComponent ans : qi.getAnswerOption()) { // XhtmlNode li = ul.li(); // render(li, ans.getValue()); // if (ans.getInitialSelected()) { -// li.tx(/*!#*/" (initially selected)"); +// li.tx(context.formatMessage(RenderingContext.QUEST_INITIALLY)); // } // } // } @@ -738,7 +738,7 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { // // appearance // if (qi.hasExtension(" http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-displayCategory")) { // XhtmlNode tr = tbl.tr(); -// tr.td().ah("http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-displayCategory").tx(/*!#*/"Display Category"); +// tr.td().ah("http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-displayCategory").tx(context.formatMessage(RenderingContext.QUEST_DISPLAY_CAT)); // render(tr.td(), qi.getExtensionByUrl("http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-displayCategory").getValue()); // } // if (ToolingExtensions.readBoolExtension(qi, "http://hl7.org/fhir/StructureDefinition/QuestionnaireResponse-hidden")) { @@ -763,14 +763,14 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { // } // if (qi.hasExtension("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-observationLinkPeriod")) { // XhtmlNode tr = tbl.tr(); -// tr.td().ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-observationLinkPeriod").tx(/*!#*/"Observation Link Period"); +// tr.td().ah(getSDCLink("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-observationLinkPeriod").tx(context.formatMessage(RenderingContext.QUEST_OBSERVATION)); // render(tr.td(), qi.getExtensionByUrl("http://hl7.org/fhir/uv/sdc/StructureDefinition/sdc-QuestionnaireResponse-observationLinkPeriod").getValue()); // } // // // dynamic management // if (qi.hasEnableWhen()) { // XhtmlNode tr = tbl.tr(); -// tr.td().tx(/*!#*/"Enable When"); +// tr.td().tx(context.formatMessage(RenderingContext.QUEST_ENABLE)); // td = tr.td(); // if (qi.getEnableWhen().size() == 1) { // renderEnableWhen(td, qi.getEnableWhen().get(0)); @@ -876,12 +876,12 @@ public class QuestionnaireResponseRenderer extends ResourceRenderer { @Override public String display(Resource r) throws UnsupportedEncodingException, IOException { - return /*!#*/"todo"; + return context.formatMessage(RenderingContext.QUEST_TODO); } @Override public String display(ResourceWrapper r) throws UnsupportedEncodingException, IOException { - return /*!#*/"Not done yet"; + return context.formatMessage(RenderingContext.QUEST_NOT_DONE); } } \ No newline at end of file diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java index f5fbf8b0e..19ff12b9c 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/Renderer.java @@ -142,25 +142,25 @@ public class Renderer { XhtmlNode td = tr.td(); XhtmlNode span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", (context.formatMessage(RenderingContext.REND_ROW_SINCE, context.getChangeVersion()))); span.img("icon-change-add.png", "icon"); - span.tx(" "+/*!#*/"Added:"); + span.tx(" "+ context.formatMessage(RenderingContext.REND_ADDED)); XhtmlNode x = new XhtmlNode(NodeType.Element, "holder"); - x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", /*!#*/"This row of content has been added since "+context.getChangeVersion()).tx(" "); + x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatMessage(RenderingContext.REND_ROW_SINCE, context.getChangeVersion())).tx(" "); tr.styleCells(x); return td; case Changed: td = tr.td(); - span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", /*!#*/"This row of content has been changed since"+context.getChangeVersion()+(vca.getOriginal() != null ? " (was '"+vca.getOriginal()+"')" : "")); + span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatMessage(RenderingContext.REND_ROW_CHANGED_SINCE_WAS, context.getChangeVersion(), vca.getOriginal())); span.img("icon-change-edit.png", "icon"); - span.tx(" "+/*!#*/"Changed:"); + span.tx(" "+ context.formatMessage(RenderingContext.REND_CHANGED)); return td; case Deleted: tr.style("text-decoration: line-through"); td = tr.td(); - span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", /*!#*/"This content has been removed since "+context.getChangeVersion()); + span = td.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatMessage(RenderingContext.REND_ROW_REMOVED_SINCE, context.getChangeVersion())); span.img("icon-change-remove.png", "icon"); - span.tx(" "+/*!#*/"Removed:"); + span.tx(" "+ context.formatMessage(RenderingContext.REND_REMOVED)); x = new XhtmlNode(NodeType.Element, "holder"); - x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px; text-decoration: none", /*!#*/"This row of content has been added since "+context.getChangeVersion()).tx(" "); + x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px; text-decoration: none", context.formatMessage(RenderingContext.REND_ROW_SINCE, context.getChangeVersion())).tx(" "); tr.styleCells(x); return td; default: @@ -168,29 +168,30 @@ public class Renderer { } } - public static void renderStatusSummary(Base base, XhtmlNode x, String version, String... metadataFields) { + public static void renderStatusSummary(RenderingContext context, Base base, XhtmlNode x, String version, String... metadataFields) { if (base.hasUserData(VersionComparisonAnnotation.USER_DATA_NAME)) { VersionComparisonAnnotation self = (VersionComparisonAnnotation) base.getUserData(VersionComparisonAnnotation.USER_DATA_NAME); switch (self.getType()) { case Added: - XhtmlNode spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", /*!#*/"This content has been added since "+version); + XhtmlNode spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatMessage(RenderingContext.REND_SINCE_ADDED, version)); spanInner.img("icon-change-add.png", "icon"); - spanInner.tx(" "+/*!#*/"Added"); + spanInner.tx(" "+context.formatMessage(RenderingContext.REND_ADDED)); return; case Changed: if (self.getComp().noChangeOtherThanMetadata(metadataFields)) { x.span("color: #eeeeee").tx("n/c"); return; } else { - spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", /*!#*/"This content has been changed since "+version+(self.getOriginal() != null ? " (was '"+(self.getOriginal())+"')" : "")); + spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", + self.getOriginal() != null ? context.formatMessage(RenderingContext.REND_SINCE_CHANGED_WAS, version, self.getOriginal()) : context.formatMessage(RenderingContext.REND_SINCE_CHANGED, version)); spanInner.img("icon-change-edit.png", "icon"); - spanInner.tx(" "+/*!#*/"Changed"); + spanInner.tx(" "+context.formatMessage(RenderingContext.REND_CHANGED)); } return; case Deleted: - spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", /*!#*/"This content has been added since "+version); + spanInner = x.span("background-color: #fff2ff; border-left: solid 3px #ffa0ff; margin: 2px; padding: 2px", context.formatMessage(RenderingContext.REND_SINCE_DELETED, version)); spanInner.img("icon-change-remove.png", "icon"); - spanInner.tx(" "+/*!#*/"Removed"); + spanInner.tx(" "+context.formatMessage(RenderingContext.REND_REMOVED)); return; default: x.span("color: #eeeeee").tx("n/c"); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java index f55c11a8c..e7c675c59 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ResourceRenderer.java @@ -232,11 +232,11 @@ public abstract class ResourceRenderer extends DataRenderer { CanonicalResource cr = (CanonicalResource) target; if (url.contains("|")) { if (target.hasWebPath()) { - x.ah(target.getWebPath()).tx(cr.present()+/*!#*/" (version "+cr.getVersion()+")"); + x.ah(target.getWebPath()).tx(cr.present()+ context.formatMessage(RenderingContext.RES_REND_VER) +cr.getVersion()+")"); } else { url = url.substring(0, url.indexOf("|")); x.code().tx(url); - x.tx(": "+cr.present()+/*!#*/" (version "+cr.getVersion()+")"); + x.tx(": "+cr.present()+ context.formatMessage(RenderingContext.RES_REND_VER) +cr.getVersion()+")"); } } else { if (target.hasWebPath()) { @@ -663,28 +663,28 @@ public abstract class ResourceRenderer extends DataRenderer { p.tx("\""+id+"\" "); } if (versionId != null) { - p.tx(/*!#*/"Version \""+versionId+"\" "); + p.tx(context.formatMessage(RenderingContext.RES_REND_VERSION) + "\""+versionId+"\" "); } if (lastUpdated != null) { - p.tx(/*!#*/"Updated \""); + p.tx(context.formatMessage(RenderingContext.RES_REND_UPDATED) + "\""); renderDateTime(p, lastUpdated); p.tx("\" "); } if (lang != null) { - p.tx(/*!#*/" (Language \""+lang+"\") "); + p.tx(" " + context.formatMessage(RenderingContext.RES_REND_LANGUAGE) + "\""+lang+"\") "); } } if (ir != null) { - plateStyle(div.para()).b().tx(/*!#*/"Special rules apply: "+ir+"!"); + plateStyle(div.para()).b().tx(context.formatMessage(RenderingContext.RES_REND_SPEC_RULES) + " "+ir+"!"); } if (source != null) { - plateStyle(div.para()).tx(/*!#*/"Information Source: "+source+"!"); + plateStyle(div.para()).tx(context.formatMessage(RenderingContext.RES_REND_INFO_SOURCE) + " "+source+"!"); } if (meta != null) { PropertyWrapper pl = meta.getChildByName("profile"); if (pl.hasValues()) { XhtmlNode p = plateStyle(div.para()); - p.tx(Utilities.pluralize(/*!#*/"Profile", pl.getValues().size())+": "); + p.tx(Utilities.pluralize(context.formatMessage(RenderingContext.RES_REND_PROFILE), pl.getValues().size())+": "); boolean first = true; for (BaseWrapper bw : pl.getValues()) { if (first) first = false; else p.tx(", "); @@ -694,7 +694,7 @@ public abstract class ResourceRenderer extends DataRenderer { PropertyWrapper tl = meta.getChildByName("tag"); if (tl.hasValues()) { XhtmlNode p = plateStyle(div.para()); - p.tx(Utilities.pluralize(/*!#*/"Tag", tl.getValues().size())+": "); + p.tx(Utilities.pluralize(context.formatMessage(RenderingContext.RES_REND_TAG), tl.getValues().size())+": "); boolean first = true; for (BaseWrapper bw : tl.getValues()) { if (first) first = false; else p.tx(", "); @@ -708,7 +708,7 @@ public abstract class ResourceRenderer extends DataRenderer { PropertyWrapper sl = meta.getChildByName("security"); if (sl.hasValues()) { XhtmlNode p = plateStyle(div.para()); - p.tx(Utilities.pluralize(/*!#*/"Security Label", tl.getValues().size())+": "); + p.tx(Utilities.pluralize(context.formatMessage(RenderingContext.RES_REND_SECURITY_LABEL), tl.getValues().size())+": "); boolean first = true; for (BaseWrapper bw : sl.getValues()) { if (first) first = false; else p.tx(", "); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SearchParameterRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SearchParameterRenderer.java index acb40586f..acfeb563f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SearchParameterRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/SearchParameterRenderer.java @@ -55,7 +55,7 @@ public class SearchParameterRenderer extends TerminologyRenderer { XhtmlNode tbl = x.table("grid"); XhtmlNode tr = tbl.tr(); - tr.td().tx(Utilities.pluralize(/*!#*/"Resource", spd.getBase().size())); + tr.td().tx(Utilities.pluralize(context.formatMessage(RenderingContext.SEARCH_PAR_REND_RES), spd.getBase().size())); XhtmlNode td = tr.td(); for (Enumeration t : spd.getBase()) { StructureDefinition sd = context.getWorker().fetchTypeDefinition(t.getCode()); @@ -81,7 +81,7 @@ public class SearchParameterRenderer extends TerminologyRenderer { } if (spd.hasTarget()) { tr = tbl.tr(); - tr.td().tx(Utilities.pluralize(/*!#*/"Target Resources", spd.getTarget().size())); + tr.td().tx(Utilities.pluralize(context.formatMessage(RenderingContext.SEARCH_PAR_REND_TARGET), spd.getTarget().size())); td = tr.td(); if (isAllConcreteResources(spd.getTarget())) { td.ah(Utilities.pathURL(context.getLink(KnownLinkType.SPEC), "resourcelist.html")).tx(context.formatMessage(RenderingContext.SEARCH_PAR_RES)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java index e534dec18..2c3f76ff4 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureDefinitionRenderer.java @@ -86,6 +86,7 @@ import org.hl7.fhir.utilities.TextFile; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.VersionUtilities; import org.hl7.fhir.utilities.i18n.I18nConstants; +import org.hl7.fhir.utilities.i18n.RenderingI18nContext; import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Cell; @@ -491,7 +492,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { if (!url.equals(source.getUrl())) { source = context.getWorker().fetchResource(StructureDefinition.class, url, source); if (source == null) { - throw new FHIRException(/*!#*/"Unable to resolve StructureDefinition "+url+" resolving content reference "+contentReference); + throw new FHIRException(context.formatMessage(RenderingContext.STRUC_DEF_REND_UNABLE_RES, url, contentReference)); } elements = source.getSnapshot().getElement(); } @@ -504,7 +505,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { return new ElementInStructure(source, ed); } } - throw new Error(/*!#*/"getElementByName: can't find "+contentReference+" in "+elements.toString()+" from "+source.getUrl()); + throw new Error(context.formatMessage(RenderingContext.STRUC_DEF_CANT_FIND, contentReference, elements.toString(), source.getUrl())); // return null; } @@ -708,7 +709,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { model.setDocoRef(Utilities.pathURL("https://build.fhir.org/ig/FHIR/ig-guidance", "readingIgs.html#table-views")); model.getTitles().add(gen.new Title(null, model.getDocoRef(), (context.formatMessage(RenderingContext.STRUC_DEF_NAME)), (context.formatMessage(RenderingContext.STRUC_DEF_LOGIC_NAME)), null, 0)); for (Column col : columns) { - model.getTitles().add(gen.new Title(null, model.getDocoRef(), (/*!#*/col.title), (/*!#*/col.hint), null, 0)); + model.getTitles().add(gen.new Title(null, model.getDocoRef(), (col.title), (col.hint), null, 0)); } return model; } @@ -738,52 +739,52 @@ public class StructureDefinitionRenderer extends ResourceRenderer { boolean ext = false; if (tail(element.getPath()).equals("extension") && isExtension(element)) { if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue())) - row.setIcon("icon_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); + row.setIcon("icon_extension_complex.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_COMPLEX)); else - row.setIcon("icon_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); + row.setIcon("icon_extension_simple.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_SIMPLE)); ext = true; } else if (tail(element.getPath()).equals("modifierExtension")) { if (element.hasType() && element.getType().get(0).hasProfile() && extensionIsComplex(element.getType().get(0).getProfile().get(0).getValue())) - row.setIcon("icon_modifier_extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); + row.setIcon("icon_modifier_extension_complex.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_COMPLEX)); else - row.setIcon("icon_modifier_extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); + row.setIcon("icon_modifier_extension_simple.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_SIMPLE)); } else if (!hasDef || element.getType().size() == 0) { if (root && profile != null && context.getWorker().getResourceNames().contains(profile.getType())) { - row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); + row.setIcon("icon_resource.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_RESOURCE)); } else if (hasDef && element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { - row.setIcon("icon-object-box.png", HierarchicalTableGenerator.TEXT_ICON_OBJECT_BOX); + row.setIcon("icon-object-box.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_OBJECT_BOX)); keyRows.add(element.getId()+"."+ToolingExtensions.readStringExtension(element, ToolingExtensions.EXT_JSON_PROP_KEY)); } else { - row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); + row.setIcon("icon_element.gif", context.formatMessage(RenderingI18nContext.TEXT_ICON_ELEMENT)); } } else if (hasDef && element.getType().size() > 1) { if (allAreReference(element.getType())) { - row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + row.setIcon("icon_reference.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_REFERENCE)); } else if (element.hasExtension(ToolingExtensions.EXT_JSON_PRIMITIVE_CHOICE)) { - row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); + row.setIcon("icon_choice.gif", context.formatMessage(RenderingI18nContext.TEXT_ICON_CHOICE)); } else { - row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); + row.setIcon("icon_choice.gif", context.formatMessage(RenderingI18nContext.TEXT_ICON_CHOICE)); typesRow = row; } } else if (hasDef && element.getType().get(0).getWorkingCode() != null && element.getType().get(0).getWorkingCode().startsWith("@")) { - row.setIcon("icon_reuse.png", HierarchicalTableGenerator.TEXT_ICON_REUSE); + row.setIcon("icon_reuse.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_REUSE)); } else if (hasDef && context.getContext().isPrimitiveType(element.getType().get(0).getWorkingCode())) { if (keyRows.contains(element.getId())) { - row.setIcon("icon-key.png", HierarchicalTableGenerator.TEXT_ICON_KEY); + row.setIcon("icon-key.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_KEY)); } else { - row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); + row.setIcon("icon_primitive.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_PRIMITIVE)); } } else if (hasDef && element.getType().get(0).hasTarget()) { - row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + row.setIcon("icon_reference.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_REFERENCE)); } else if (hasDef && context.getContext().isDataType(element.getType().get(0).getWorkingCode())) { - row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); + row.setIcon("icon_datatype.gif", context.formatMessage(RenderingI18nContext.TEXT_ICON_DATATYPE)); } else if (hasDef && element.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { - row.setIcon("icon-object-box.png", HierarchicalTableGenerator.TEXT_ICON_OBJECT_BOX); + row.setIcon("icon-object-box.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_OBJECT_BOX)); keyRows.add(element.getId()+"."+ToolingExtensions.readStringExtension(element, ToolingExtensions.EXT_JSON_PROP_KEY)); } else if (hasDef && Utilities.existsInList(element.getType().get(0).getWorkingCode(), "Base", "Element", "BackboneElement")) { - row.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); + row.setIcon("icon_element.gif", context.formatMessage(RenderingI18nContext.TEXT_ICON_ELEMENT)); } else { - row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); + row.setIcon("icon_resource.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_RESOURCE)); } if (element.hasUserData("render.opaque")) { row.setOpacity("0.5"); @@ -823,7 +824,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { showMissing = false; //? slicingRow = row; } else { - row.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE); + row.setIcon("icon_slice.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_SLICE)); slicingRow = row; for (Cell cell : row.getCells()) for (Piece p : cell.getPieces()) { @@ -831,7 +832,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } } } else if (element.hasSliceName()) { - row.setIcon("icon_slice_item.png", HierarchicalTableGenerator.TEXT_ICON_SLICE_ITEM); + row.setIcon("icon_slice_item.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_SLICE_ITEM)); } if (used.used || showMissing) rows.add(row); @@ -849,8 +850,8 @@ public class StructureDefinitionRenderer extends ResourceRenderer { hrow.setAnchor(element.getPath()); hrow.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); hrow.setLineColor(1); - hrow.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); - hrow.getCells().add(gen.new Cell(null, null, sName+/*!#*/":All Slices", "", null)); + hrow.setIcon("icon_element.gif", context.formatMessage(RenderingI18nContext.TEXT_ICON_ELEMENT)); + hrow.getCells().add(gen.new Cell(null, null, sName+ (context.formatMessage(RenderingContext.STRUC_DEF_ALL_SLICES)), "", null)); switch (context.getStructureMode()) { case BINDINGS: case OBLIGATIONS: @@ -875,7 +876,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { hrow.setAnchor(element.getPath()); hrow.setColor(context.getProfileUtilities().getRowColor(element, isConstraintMode)); hrow.setLineColor(1); - hrow.setIcon("icon_element.gif", HierarchicalTableGenerator.TEXT_ICON_ELEMENT); + hrow.setIcon("icon_element.gif", context.formatMessage(RenderingI18nContext.TEXT_ICON_ELEMENT)); hrow.getCells().add(gen.new Cell(null, null, sName+ context.formatMessage(RenderingContext.STRUC_DEF_ALL_TYPES), "", null)); switch (context.getStructureMode()) { case BINDINGS: @@ -908,7 +909,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { parent.setAnchor(child.getPath()); parent.setColor(context.getProfileUtilities().getRowColor(child, isConstraintMode)); parent.setLineColor(1); - parent.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE); + parent.setIcon("icon_slice.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_SLICE)); parent.getCells().add(gen.new Cell(null, null, "Slices for "+ child.getName(), "", null)); switch (context.getStructureMode()) { case BINDINGS: @@ -1089,13 +1090,13 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } if (element != null && (hasNonBaseConstraints(element.getConstraint()) || hasNonBaseConditions(element.getCondition()))) { Piece p = gc.addText(CONSTRAINT_CHAR); - p.setHint((/*!#*/"This element has or is affected by constraints ("+listConstraintsAndConditions(element)+")")); + p.setHint((context.formatMessage(RenderingContext.STRUC_DEF_ELE_AFFECTED, listConstraintsAndConditions(element), ")"))); p.addStyle(CONSTRAINT_STYLE); p.setReference(Utilities.pathURL(VersionUtilities.getSpecUrl(context.getWorker().getVersion()), "conformance-rules.html#constraints")); } if (element != null && element.hasExtension(ToolingExtensions.EXT_STANDARDS_STATUS)) { StandardsStatus ss = StandardsStatus.fromCode(element.getExtensionString(ToolingExtensions.EXT_STANDARDS_STATUS)); - gc.addStyledText(/*!#*/"Standards Status = "+ss.toDisplay(), ss.getAbbrev(), "black", ss.getColor(), context.getWorker().getSpecUrl()+"versions.html#std-process", true); + gc.addStyledText(context.formatMessage(RenderingContext.STRUC_DEF_STAND_STATUS) +ss.toDisplay(), ss.getAbbrev(), context.formatMessage(RenderingContext.STRUC_DEF_BLACK), ss.getColor(), context.getWorker().getSpecUrl()+"versions.html#std-process", true); } ExtensionContext extDefn = null; @@ -1237,7 +1238,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { row.setAnchor(parent.getPath()+"-"+grp.getName()); row.setColor(context.getProfileUtilities().getRowColor(parent, isConstraintMode)); row.setLineColor(1); - row.setIcon("icon_choice.gif", HierarchicalTableGenerator.TEXT_ICON_CHOICE); + row.setIcon("icon_choice.gif", context.formatMessage(RenderingI18nContext.TEXT_ICON_CHOICE)); row.getCells().add(gen.new Cell(null, null, context.formatMessage(RenderingContext.STRUC_DEF_CHOICE), "", null)); row.getCells().add(gen.new Cell()); row.getCells().add(gen.new Cell(null, null, (grp.isMandatory() ? "1" : "0")+"..1", "", null)); @@ -1316,7 +1317,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { if (root) { 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.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profile" : "type")+". ", null)); + c.addPiece(gen.new Piece(null, context.formatMessage(RenderingContext.STRUC_DEF_ABSTRACT) +(profile.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profile" : "type")+". ", null)); List children = new ArrayList<>(); for (StructureDefinition sd : context.getWorker().fetchResourcesByType(StructureDefinition.class)) { @@ -1325,7 +1326,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } } if (!children.isEmpty()) { - c.addPiece(gen.new Piece(null, /*!#*/"Child "+(profile.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profiles" : "types")+": ", null)); + c.addPiece(gen.new Piece(null, context.formatMessage(RenderingContext.STRUC_DEF_CHILD) +(profile.getDerivation() == TypeDerivationRule.CONSTRAINT ? "profiles" : "types")+": ", null)); boolean first = true; for (StructureDefinition sd : children) { if (first) first = false; else c.addPiece(gen.new Piece(null, ", ", null)); @@ -1511,7 +1512,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { if (definition.hasExtension(ToolingExtensions.EXT_JSON_PROP_KEY)) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } String code = ToolingExtensions.readStringExtension(definition, ToolingExtensions.EXT_JSON_PROP_KEY); - c.getPieces().add(gen.new Piece(null, /*!#*/"JSON: Represented as a single JSON Object with named properties using the value of the "+code+" child as the key", null)); + c.getPieces().add(gen.new Piece(null, context.formatMessage(RenderingContext.STRUC_DEF_SINGLE_JSON_OBJECTS, code), null)); } if (definition.hasExtension(ToolingExtensions.EXT_TYPE_SPEC)) { for (Extension e : definition.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC)) { @@ -2117,7 +2118,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { used.used = element.hasType() && element.getType().get(0).hasProfile(); showMissing = false; } else { - row.setIcon("icon_slice.png", HierarchicalTableGenerator.TEXT_ICON_SLICE); + row.setIcon("icon_slice.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_SLICE); row.getCells().get(2).getPieces().clear(); for (Cell cell : row.getCells()) for (Piece p : cell.getPieces()) { @@ -2255,15 +2256,15 @@ public class StructureDefinitionRenderer extends ResourceRenderer { row.getCells().add(c); if (!pattern) { c.addPiece(gen.new Piece(null, "0..0", null)); - row.setIcon("icon_fixed.gif", context.formatMessage(RenderingContext.STRUC_DEF_FIXED_VALUE) /*HierarchicalTableGenerator.TEXT_ICON_FIXED*/); + row.setIcon("icon_fixed.gif", context.formatMessage(RenderingContext.STRUC_DEF_FIXED_VALUE) /*context.formatMessage(RenderingI18nContext.TEXT_ICON_FIXED*/); } else if (context.getContext().isPrimitiveType(t.getTypeCode())) { - row.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); + row.setIcon("icon_primitive.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_PRIMITIVE)); c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); } else if (isReference(t.getTypeCode())) { - row.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + row.setIcon("icon_reference.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_REFERENCE)); c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); } else { - row.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); + row.setIcon("icon_datatype.gif", context.formatMessage(RenderingI18nContext.TEXT_ICON_DATATYPE)); c.addPiece(gen.new Piece(null, "0.."+(t.getMaxCardinality() == 2147483647 ? "*": Integer.toString(t.getMaxCardinality())), null)); } c = gen.new Cell(); @@ -2290,7 +2291,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { Row row = gen.new Row(); row.setId(ed.getPath()); erow.getSubRows().add(row); - row.setIcon("icon_fixed.gif", context.formatMessage(RenderingContext.STRUC_DEF_FIXED) /*HierarchicalTableGenerator.TEXT_ICON_FIXED*/); + row.setIcon("icon_fixed.gif", context.formatMessage(RenderingContext.STRUC_DEF_FIXED) /*context.formatMessage(RenderingI18nContext.TEXT_ICON_FIXED*/); Cell c = gen.new Cell(); row.getCells().add(c); @@ -2430,12 +2431,12 @@ public class StructureDefinitionRenderer extends ResourceRenderer { if (definition.hasContentReference()) { ElementInStructure ed = getElementByName(profile.getSnapshot().getElement(), definition.getContentReference(), profile); if (ed == null) - c.getPieces().add(gen.new Piece(null, /*!#*/"Unknown reference to "+definition.getContentReference(), null)); + c.getPieces().add(gen.new Piece(null, context.formatMessage(RenderingContext.STRUC_DEF_UNKNOWN_REF, definition.getContentReference()), null)); else { if (ed.getSource() == profile) { - c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), /*!#*/"See "+ed.getElement().getPath(), null)); + c.getPieces().add(gen.new Piece("#"+ed.getElement().getPath(), context.formatMessage(RenderingContext.STRUC_DEF_SEE, ed.getElement().getPath()), null)); } else { - c.getPieces().add(gen.new Piece(ed.getSource().getWebPath()+"#"+ed.getElement().getPath(), /*!#*/"See "+ed.getSource().getTypeName()+"."+ed.getElement().getPath(), null)); + c.getPieces().add(gen.new Piece(ed.getSource().getWebPath()+"#"+ed.getElement().getPath(), context.formatMessage(RenderingContext.STRUC_DEF_SEE, ed.getSource().getTypeName()) +"."+ed.getElement().getPath(), null)); } } } @@ -2509,7 +2510,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } else if (definition.hasExample()) { for (ElementDefinitionExampleComponent ex : definition.getExample()) { if (!c.getPieces().isEmpty()) { c.addPiece(gen.new Piece("br")); } - c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, /*!#*/"Example'"+("".equals("General")? "": " "+ex.getLabel()+"'")+": ", "").addStyle("font-weight:bold"))); + c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, context.formatMessage(RenderingContext.STRUC_DEF_EXAMPLE) +"'"+("".equals("General")? "": " "+ex.getLabel()+"'")+": ", "").addStyle("font-weight:bold"))); c.getPieces().add(checkForNoChange(ex, gen.new Piece(null, buildJson(ex.getValue()), null).addStyle("color: darkgreen"))); } } @@ -2694,7 +2695,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), null, null)); choicerow.getCells().add(gen.new Cell()); choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); - choicerow.setIcon("icon_reference.png", HierarchicalTableGenerator.TEXT_ICON_REFERENCE); + choicerow.setIcon("icon_reference.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_REFERENCE)); Cell c = gen.new Cell(); choicerow.getCells().add(c); if (ADD_REFERENCE_TO_TABLE) { @@ -2739,7 +2740,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); choicerow.getCells().add(gen.new Cell()); choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); - choicerow.setIcon("icon_primitive.png", HierarchicalTableGenerator.TEXT_ICON_PRIMITIVE); + choicerow.setIcon("icon_primitive.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_PRIMITIVE)); Cell c = gen.new Cell(null, corePath+"datatypes.html#"+t, sd.getTypeName(), null, null); choicerow.getCells().add(c); if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { @@ -2751,7 +2752,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { choicerow.getCells().add(gen.new Cell(null, null, tail(element.getPath()).replace("[x]", Utilities.capitalize(t)), sd.getDescription(), null)); choicerow.getCells().add(gen.new Cell()); choicerow.getCells().add(gen.new Cell(null, null, "", null, null)); - choicerow.setIcon("icon_datatype.gif", HierarchicalTableGenerator.TEXT_ICON_DATATYPE); + choicerow.setIcon("icon_datatype.gif", context.formatMessage(RenderingI18nContext.TEXT_ICON_DATATYPE)); Cell c = gen.new Cell(null, context.getPkp().getLinkFor(corePath, t), sd.getTypeName(), null, null); choicerow.getCells().add(c); if (!mustSupportMode && isMustSupport(tr) && element.getMustSupport()) { @@ -2993,9 +2994,9 @@ public class StructureDefinitionRenderer extends ResourceRenderer { row.setAnchor(span.getId()); //row.setColor(..?); if (span.isProfile()) { - row.setIcon("icon_profile.png", HierarchicalTableGenerator.TEXT_ICON_PROFILE); + row.setIcon("icon_profile.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_PROFILE)); } else { - row.setIcon("icon_resource.png", HierarchicalTableGenerator.TEXT_ICON_RESOURCE); + row.setIcon("icon_resource.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_RESOURCE)); } row.getCells().add(gen.new Cell(null, null, span.getName(), null, null)); @@ -3145,7 +3146,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { if (full || vdeep) { r.getCells().add(gen.new Cell("", "", "Extension", null, null)); - r.setIcon(deep ? "icon_"+m+"extension_complex.png" : "icon_extension_simple.png", deep ? HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX : HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); + r.setIcon(deep ? "icon_"+m+"extension_complex.png" : "icon_extension_simple.png", deep ? context.formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_COMPLEX) : context.formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_SIMPLE)); List children = getChildren(ed.getSnapshot().getElement(), ed.getSnapshot().getElement().get(0)); for (ElementDefinition child : children) if (!child.getPath().endsWith(".id")) { @@ -3161,7 +3162,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { } r.getCells().add(gen.new Cell("", "", "Extension", null, null)); - r.setIcon("icon_"+m+"extension_complex.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_COMPLEX); + r.setIcon("icon_"+m+"extension_complex.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_COMPLEX)); for (ElementDefinition c : children) { ved = getValueFor(ed, c); @@ -3174,7 +3175,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { r1.getCells().add(gen.new Cell()); r1.getCells().add(gen.new Cell(null, null, describeCardinality(c, null, new UnusedTracker()), null, null)); genTypes(gen, r1, ved, defFile, ed, corePath, imagePath, false, false); - r1.setIcon("icon_"+m+"extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); + r1.setIcon("icon_"+m+"extension_simple.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_SIMPLE)); generateDescription(gen, r1, c, null, true, corePath, corePath, ed, corePath, imagePath, false, false, false, ved, false, false, false, rc); } } @@ -3186,7 +3187,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { genTypes(gen, r, ved, defFile, ed, corePath, imagePath, false, false); - r.setIcon("icon_"+m+"extension_simple.png", HierarchicalTableGenerator.TEXT_ICON_EXTENSION_SIMPLE); + r.setIcon("icon_"+m+"extension_simple.png", context.formatMessage(RenderingI18nContext.TEXT_ICON_EXTENSION_SIMPLE)); } Cell c = gen.new Cell("", "", "URL = "+ed.getUrl(), null, null); Piece cc = gen.new Piece(null, ed.getName()+": ", null); @@ -3665,7 +3666,7 @@ public class StructureDefinitionRenderer extends ResourceRenderer { tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_CONTROL), "conformance-rules.html#conformance", strikethrough, describeCardinality(d, compare, mode)); tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_BINDING), "terminologies.html", strikethrough, describeBinding(sd, d, d.getPath(), compare, mode)); if (d.hasContentReference()) { - tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_TYPE), null, strikethrough, /*!#*/"See " + d.getContentReference().substring(1)); + tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_TYPE), null, strikethrough, context.formatMessage(RenderingContext.STRUC_DEF_SEE) + d.getContentReference().substring(1)); } else { tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_TYPE), "datatypes.html", strikethrough, describeTypes(d.getType(), false, d, compare, mode, value, compareValue, sd)); } @@ -3673,18 +3674,19 @@ public class StructureDefinitionRenderer extends ResourceRenderer { tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_DEFAULT_TYPE), "datatypes.html", strikethrough, ToolingExtensions.readStringExtension(d, ToolingExtensions.EXT_DEF_TYPE)); } if (d.hasExtension(ToolingExtensions.EXT_TYPE_SPEC)) { - tableRow(tbl, Utilities.pluralize(/*!#*/"Type Specifier", d.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC).size()), "datatypes.html", strikethrough, formatTypeSpecifiers(d)); + tableRow(tbl, Utilities.pluralize(context.formatMessage(RenderingContext.STRUC_DEF_TYPE_SPEC), d.getExtensionsByUrl(ToolingExtensions.EXT_TYPE_SPEC).size()), "datatypes.html", strikethrough, formatTypeSpecifiers(d)); } if (d.getPath().endsWith("[x]") && !d.prohibited()) { - tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_NOTE_X), null, strikethrough).ahWithText("See ", spec("formats.html#choice"), null, /*!#*/"Choice of Data Types", " for further information about how to use [x]"); + tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_NOTE_X), null, strikethrough).ahWithText(context.formatMessage(RenderingContext.STRUC_DEF_SEE) + , spec("formats.html#choice"), null, context.formatMessage(RenderingContext.STRUC_DEF_CHOICE_DATA_TYPE), context.formatMessage(RenderingContext.STRUC_DEF_FURTHER_INFO)); } tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_MODIFIER), "conformance-rules.html#ismodifier", strikethrough, presentModifier(d, mode, compare)); if (d.getMustHaveValue()) { - tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_PRIMITIVE), "elementdefinition.html#primitives", strikethrough, /*!#*/"This primitive type must have a value (the value must be present, and cannot be replaced by an extension)"); + tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_PRIMITIVE), "elementdefinition.html#primitives", strikethrough, context.formatMessage(RenderingContext.STRUC_DEF_PRIM_TYPE_VALUE)); } else if (d.hasValueAlternatives()) { - tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_PRIMITIVE), "elementdefinition.html#primitives", strikethrough, renderCanonicalList(/*!#*/"This primitive type may be present, or absent if replaced by one of the following extensions: ", d.getValueAlternatives())); + tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_PRIMITIVE), "elementdefinition.html#primitives", strikethrough, renderCanonicalList(context.formatMessage(RenderingContext.STRUC_DEF_PRIM_TYPE_PRESENT), d.getValueAlternatives())); } else if (hasPrimitiveTypes(d)) { - tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_PRIMITIVE), "elementdefinition.html#primitives", strikethrough, /*!#*/"This primitive element may be present, or absent, or replaced by an extension"); + tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_PRIMITIVE), "elementdefinition.html#primitives", strikethrough, context.formatMessage(RenderingContext.STRUC_DEF_PRIM_ELE)); } if (ToolingExtensions.hasAllowedUnits(d)) { tableRow(tbl, context.formatMessage(RenderingContext.STRUC_DEF_ALLOWED), "http://hl7.org/fhir/extensions/StructureDefinition-elementdefinition-allowedUnits.html", strikethrough, describeAllowedUnits(d)); diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureMapRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureMapRenderer.java index fae63e3b2..b11bbf6f7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureMapRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/StructureMapRenderer.java @@ -61,9 +61,6 @@ public class StructureMapRenderer extends TerminologyRenderer { private static final String COLOR_SYNTAX = "navy"; private static final boolean MULTIPLE_TARGETS_ONELINE = true; private static final String COLOR_SPECIAL = "#b36b00"; - private static final String DEFAULT_COMMENT = /*!#*/"This element was not defined prior to R5"; - - private String clauseComment = DEFAULT_COMMENT; public StructureMapRenderer(RenderingContext context) { super(context); @@ -634,7 +631,7 @@ public class StructureMapRenderer extends TerminologyRenderer { } if (isClause) { XhtmlNode s= x.color(COLOR_SPECIAL); - s.setAttribute("title", clauseComment ); + s.setAttribute("title", formatMessage(RenderingContext.MAP_DEFAULT_COMMENT)); s.tx("// "); s.tx(doco.replace("\r\n", " ").replace("\r", " ").replace("\n", " ")); } else { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TerminologyRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TerminologyRenderer.java index 292153b3e..40f63e0ce 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TerminologyRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TerminologyRenderer.java @@ -329,7 +329,7 @@ public abstract class TerminologyRenderer extends ResourceRenderer { protected void clipboard(XhtmlNode x, String img, String title, String source) { - XhtmlNode span = x.span("cursor: pointer", /*!#*/"Copy "+title+" Format to clipboard"); + XhtmlNode span = x.span("cursor: pointer", formatMessage(RenderingContext.TERM_REND_COPY, title)); span.attribute("onClick", "navigator.clipboard.writeText('"+Utilities.escapeJson(source)+"');"); span.img(img, "btn").setAttribute("width", "24px").setAttribute("height", "16px"); } diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TestPlanRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TestPlanRenderer.java index 4bd97d560..73fee97a7 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TestPlanRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/TestPlanRenderer.java @@ -132,7 +132,7 @@ public class TestPlanRenderer extends ResourceRenderer { if (tp.hasTestCase()) { for (TestPlanTestCaseComponent tc : tp.getTestCase()) { - x.h2().addText(/*!#*/"Test Case" + (tc.hasSequence() ? " - Sequence" + tc.getSequence() : "")); + x.h2().addText(tc.hasSequence() ? formatMessage(RenderingContext.TEST_PLAN_CASE) : formatMessage(RenderingContext.TEST_PLAN_CASE_SEQ, tc.getSequence())); if (tc.hasScope()) { if (tc.getScope().size() == 1) { diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java index ea6d37bf4..2e1685c28 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/renderers/ValueSetRenderer.java @@ -60,6 +60,7 @@ import org.hl7.fhir.r5.utils.ToolingExtensions; import org.hl7.fhir.utilities.LoincLinker; import org.hl7.fhir.utilities.Utilities; import org.hl7.fhir.utilities.i18n.I18nConstants; +import org.hl7.fhir.utilities.i18n.RenderingI18nContext; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.Row; import org.hl7.fhir.utilities.xhtml.HierarchicalTableGenerator.TableModel; @@ -78,8 +79,6 @@ public class ValueSetRenderer extends TerminologyRenderer { super(context, rcontext); } - private static final String ABSTRACT_CODE_HINT = /*!#*/"This code is not selectable ('Abstract')"; - private static final int MAX_DESIGNATIONS_IN_LINE = 5; private static final int MAX_BATCH_VALIDATION_SIZE = 1000; @@ -209,7 +208,7 @@ public class ValueSetRenderer extends TerminologyRenderer { if (vs.getExpansion().getContains().isEmpty()) { msg = context.formatMessage(RenderingContext.VALUE_SET_TOO_COSTLY); } else { - msg = /*!#*/"This value set cannot be fully expanded, but a selection ("+countMembership(vs)+" codes) of the whole set of codes is shown here."; + msg = context.formatMessage(RenderingContext.VALUE_SET_CODE_SELEC, countMembership(vs)); } x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(msg); } else { @@ -217,9 +216,9 @@ public class ValueSetRenderer extends TerminologyRenderer { if (vs.getExpansion().hasTotal()) { if (count != vs.getExpansion().getTotal()) { x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px") - .addText(/*!#*/"This value set has "+(hasFragment ? "at least " : "")+vs.getExpansion().getTotal()+" codes in it. In order to keep the publication size manageable, only a selection ("+count+" codes) of the whole set of codes is shown."); + .addText(context.formatMessage(RenderingContext.VALUE_SET_HAS)+(hasFragment ? context.formatMessage(RenderingContext.VALUE_SET_AT_LEAST) : "")+vs.getExpansion().getTotal()+" codes in it. In order to keep the publication size manageable, only a selection ("+count+" codes) of the whole set of codes is shown."); } else { - x.para().tx(/*!#*/"This value set contains "+(hasFragment ? "at least " : "")+vs.getExpansion().getTotal()+" concepts."); + x.para().tx(context.formatMessage(RenderingContext.VALUE_SET_CONTAINS)+(hasFragment ? context.formatMessage(RenderingContext.VALUE_SET_AT_LEAST) : "")+vs.getExpansion().getTotal()+" concepts."); } } else if (count == 1000) { // it's possible that there's exactly 1000 codes, in which case wht we're about to do is wrong @@ -227,7 +226,7 @@ public class ValueSetRenderer extends TerminologyRenderer { String msg = context.formatMessage(RenderingContext.VALUE_SET_SEL); x.para().style("border: maroon 1px solid; background-color: #FFCCCC; font-weight: bold; padding: 8px").addText(msg); } else { - x.para().tx(/*!#*/"This value set expansion contains "+count+" concepts."); + x.para().tx(context.formatMessage(RenderingContext.VALUE_SET_NUMBER_CONCEPTS, count)); } } @@ -514,30 +513,30 @@ public class ValueSetRenderer extends TerminologyRenderer { if (parts.length >= 5) { String m = describeModule(parts[4]); if (parts.length == 7) { - x.tx(/*!#*/"SNOMED CT "+m+" edition "+formatSCTDate(parts[6])); + x.tx(context.formatMessage(RenderingContext.VALUE_SET_SNOMED_ADD, m, formatSCTDate(parts[6]))); } else { - x.tx(/*!#*/"SNOMED CT "+m+" edition"); + x.tx(context.formatMessage(RenderingContext.VALUE_SET_SNOMED, m)); } } else { - x.tx(displaySystem(u)+" "+/*!#*/"version "+v); + x.tx(displaySystem(u)+" "+ context.formatMessage(RenderingContext.VALUE_SET_VERSION) + " " +v); } } else if (u.equals("http://loinc.org")) { String vd = describeLoincVer(v); if (vd != null) { - x.tx(/*!#*/"Loinc v"+v+" ("+vd+")"); + x.tx(context.formatMessage(RenderingContext.VALUE_SET_LOINCV)+v+" ("+vd+")"); } else { - x.tx(/*!#*/"Loinc v"+v); + x.tx(context.formatMessage(RenderingContext.VALUE_SET_LOINCV)+v); } } else if (Utilities.noString(v)) { CanonicalResource cr = (CanonicalResource) getContext().getWorker().fetchResource(Resource.class, u, source); if (cr != null) { if (cr.hasWebPath()) { - x.ah(cr.getWebPath()).tx(t+" "+cr.present()+" "+/*!#*/"(no version) ("+cr.fhirType()+")"); + x.ah(cr.getWebPath()).tx(t+" "+cr.present()+" "+ context.formatMessage(RenderingContext.VALUE_SET_NO_VERSION)+cr.fhirType()+")"); } else { - x.tx(t+" "+displaySystem(u)+" "+/*!#*/"(no version) ("+cr.fhirType()+")"); + x.tx(t+" "+displaySystem(u)+" "+context.formatMessage(RenderingContext.VALUE_SET_NO_VERSION)+cr.fhirType()+")"); } } else { - x.tx(t+" "+displaySystem(u)+" "+/*!#*/"(no version)"); + x.tx(t+" "+displaySystem(u)+" "+ context.formatMessage(RenderingContext.VALUE_SET_NO_VER)); } } else { CanonicalResource cr = (CanonicalResource) getContext().getWorker().fetchResource(Resource.class, u+"|"+v, source); @@ -548,7 +547,7 @@ public class ValueSetRenderer extends TerminologyRenderer { x.tx(t+" "+displaySystem(u)+" v"+v+" ("+cr.fhirType()+")"); } } else { - x.tx(t+" "+displaySystem(u)+" "+/*!#*/"version "+v); + x.tx(t+" "+displaySystem(u)+" "+ context.formatMessage(RenderingContext.VALUE_SET_VERSION)+v); } } } @@ -620,7 +619,7 @@ public class ValueSetRenderer extends TerminologyRenderer { private String describeModule(String module) { if ("900000000000207008".equals(module)) - return /*!#*/context.formatMessage(RenderingContext.VALUE_SET_INT); + return context.formatMessage(RenderingContext.VALUE_SET_INT); if ("731000124108".equals(module)) return context.formatMessage(RenderingContext.VALUE_SET_US); if ("32506021000036107".equals(module)) @@ -897,7 +896,7 @@ public class ValueSetRenderer extends TerminologyRenderer { CodeSystem e = getContext().getWorker().fetchCodeSystem(system); if (e == null || (e.getContent() != org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode.COMPLETE && e.getContent() != org.hl7.fhir.r5.model.Enumerations.CodeSystemContentMode.FRAGMENT)) { if (isAbstract) - td.i().setAttribute("title", ABSTRACT_CODE_HINT).addText(code); + td.i().setAttribute("title", context.formatMessage(RenderingI18nContext.VS_ABSTRACT_CODE_HINT)).addText(code); else if ("http://snomed.info/sct".equals(system)) { td.ah(sctLink(code)).addText(code); } else if ("http://loinc.org".equals(system)) { @@ -911,7 +910,7 @@ public class ValueSetRenderer extends TerminologyRenderer { else href = href + "#"+e.getId()+"-"+Utilities.nmtokenize(code); if (isAbstract) - td.ah(href).setAttribute("title", ABSTRACT_CODE_HINT).i().addText(code); + td.ah(href).setAttribute("title", context.formatMessage(RenderingI18nContext.VS_ABSTRACT_CODE_HINT)).i().addText(code); else td.ah(href).addText(code); } @@ -1178,14 +1177,14 @@ public class ValueSetRenderer extends TerminologyRenderer { if (inc.hasSystem()) { CodeSystem e = getContext().getWorker().fetchCodeSystem(inc.getSystem()); if (inc.getConcept().size() == 0 && inc.getFilter().size() == 0) { - li.addText(type+" "+/*!#*/"all codes defined in "); + li.addText(type+" "+ context.formatMessage(RenderingContext.VALUE_SET_ALL_CODES_DEF) + " "); addCsRef(inc, li, e); } else { if (inc.getConcept().size() > 0) { - li.addText(type+" "+/*!#*/"these codes as defined in "); + li.addText(type+" "+ context.formatMessage(RenderingContext.VALUE_SET_THESE_CODES_DEF) + " "); addCsRef(inc, li, e); if (inc.hasVersion()) { - li.addText(" "+/*!#*/"version "); + li.addText(" "+ context.formatMessage(RenderingContext.VALUE_SET_VERSION) + " "); li.code(inc.getVersion()); } @@ -1212,14 +1211,14 @@ public class ValueSetRenderer extends TerminologyRenderer { } } if (inc.getFilter().size() > 0) { - li.addText(type+" "+/*!#*/"codes from "); + li.addText(type+" "+ context.formatMessage(RenderingContext.VALUE_SET_CODES_FROM)); addCsRef(inc, li, e); - li.tx(" "+/*!#*/"where "); + li.tx(" "+ context.formatMessage(RenderingContext.VALUE_SET_WHERE)+" "); for (int i = 0; i < inc.getFilter().size(); i++) { ConceptSetFilterComponent f = inc.getFilter().get(i); if (i > 0) { if (i == inc.getFilter().size()-1) { - li.tx(" "+/*!#*/"and "); + li.tx(" "+ context.formatMessage(RenderingContext.VALUE_SET_AND)); } else { li.tx(context.formatMessage(RenderingContext.VALUE_SET_COMMA)+" "); } @@ -1227,9 +1226,9 @@ public class ValueSetRenderer extends TerminologyRenderer { XhtmlNode wli = renderStatus(f, li); if (f.getOp() == FilterOperator.EXISTS) { if (f.getValue().equals("true")) { - wli.tx(f.getProperty()+" "+/*!#*/"exists"); + wli.tx(f.getProperty()+" "+ context.formatMessage(RenderingContext.VALUE_SET_EXISTS)); } else { - wli.tx(f.getProperty()+" "+/*!#*/"doesn't exist"); + wli.tx(f.getProperty()+" "+ context.formatMessage(RenderingContext.VALUE_SET_DOESNT_EXIST)); } } else { wli.tx(f.getProperty()+" "+describe(f.getOp())+" "); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java index 7216385b1..ae2302b76 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java @@ -495,6 +495,8 @@ public class RenderingI18nContext extends I18nBase { public static final String REND_CHANGED = "REND_CHANGED"; public static final String REND_REMOVED = "REND_REMOVED"; public static final String REND_ROW_SINCE = "REND_ROW_SINCE"; + public static final String REND_ROW_CHANGED_SINCE = "REND_ROW_CHANGED_SINCE"; + public static final String REND_ROW_REMOVED_SINCE = "REND_ROW_REMOVED_SINCE"; public static final String REQ_ACTOR = "REQ_ACTOR"; public static final String REQ_FOLLOWING_ACTOR = "REQ_FOLLOWING_ACTOR"; public static final String REQ_DERIVE = "REQ_DERIVE"; @@ -905,7 +907,160 @@ public class RenderingI18nContext extends I18nBase { public static final String CAPABILITY_SEARCH_PARS = "CAPABILITY_SEARCH_PARS"; public static final String CAPABILITY_COMB_SEARCH_PAR = "CAPABILITY_COMB_SEARCH_PAR"; public static final String CODE_SYS_IN_A_HIERARCHY = "CODE_SYS_IN_A_HIERARCHY"; - + public static final String CODE_SYS_REPLACED_BY = "CODE_SYS_REPLACED_BY"; + public static final String DATA_REND_ONGOING = "DATA_REND_ONGOING"; + public static final String EXAMPLE_SCEN_UNABLE_TO_FIND = "EXAMPLE_SCEN_UNABLE_TO_FIND"; + public static final String EXAMPLE_SCEN_STEP_SCEN = "EXAMPLE_SCEN_STEP_SCEN"; + public static final String DATA_REND_DETAILS_STATED = "DATA_REND_DETAILS_STATED"; + public static final String DATA_REND_VERSION = "DATA_REND_VERSION"; + public static final String DATA_REND_BASE64 = "DATA_REND_BASE64"; + public static final String DATA_REND_CODES = "DATA_REND_CODES"; + public static final String DATA_REND_GLN = "DATA_REND_GLN"; + public static final String DATA_REND_DETAILS = "DATA_REND_DETAILS"; + public static final String DATA_REND_PER = "DATA_REND_PER"; + public static final String DATA_REND_EXCEPTION = "DATA_REND_EXCEPTION"; + public static final String DIAG_REP_REND_PER = "DIAG_REP_REND_PER"; + public static final String DIAG_REP_REND_IDENTIFIER = "DIAG_REP_REND_IDENTIFIER"; + public static final String DIAG_REP_REND_REQUEST = "DIAG_REP_REND_REQUEST"; + public static final String PROF_DRIV_GEN_NARR = "PROF_DRIV_GEN_NARR"; + public static final String PROF_DRIV_ERR_GEN_NARR = "PROF_DRIV_ERR_GEN_NARR"; + public static final String QUEST_UNKNOWN_MODE = "QUEST_UNKNOWN_MODE"; + public static final String QUEST_ANSWER = "QUEST_ANSWER"; + public static final String QUEST_RESP_ROOT = "QUEST_RESP_ROOT"; + public static final String QUEST_RESP = "QUEST_RESP"; + public static final String QUEST_QUESTION = "QUEST_QUESTION"; + public static final String QUEST_NONE_SPEC = "QUEST_NONE_SPEC"; + public static final String QUEST_GROUP = "QUEST_GROUP"; + public static final String QUEST_ITEM = "QUEST_ITEM"; + public static final String QUEST_TRY_QUEST = "QUEST_TRY_QUEST"; + public static final String QUEST_DISPLAY_CAT = "QUEST_DISPLAY_CAT"; + public static final String QUEST_NOT_DONE = "QUEST_NOT_DONE"; + public static final String RES_REND_VER = "RES_REND_VER"; + public static final String RES_REND_VERSION = "RES_REND_VERSION"; + public static final String RES_REND_UPDATED = "RES_REND_UPDATED"; + public static final String RES_REND_LANGUAGE = "RES_REND_LANGUAGE"; + public static final String RES_REND_SPEC_RULES = "RES_REND_SPEC_RULES"; + public static final String RES_REND_INFO_SOURCE = "RES_REND_INFO_SOURCE"; + public static final String RES_REND_PROFILE = "RES_REND_PROFILE"; + public static final String RES_REND_SECURITY_LABEL = "RES_REND_SECURITY_LABEL"; + public static final String SEARCH_PAR_REND_RES = "SEARCH_PAR_REND_RES"; + public static final String SEARCH_PAR_REND_TARGET = "SEARCH_PAR_REND_TARGET"; + public static final String STRUC_DEF_REND_UNABLE_RES = "STRUC_DEF_REND_UNABLE_RES"; + public static final String STRUC_DEF_CANT_FIND = "STRUC_DEF_CANT_FIND"; + public static final String STRUC_DEF_ALL_SLICES = "STRUC_DEF_ALL_SLICES"; + public static final String STRUC_DEF_ELE_AFFECTED = "STRUC_DEF_ELE_AFFECTED"; + public static final String STRUC_DEF_STAND_STATUS = "STRUC_DEF_STAND_STATUS"; + public static final String STRUC_DEF_BLACK = "STRUC_DEF_BLACK"; + public static final String STRUC_DEF_ABSTRACT = "STRUC_DEF_ABSTRACT"; + public static final String STRUC_DEF_CHILD = "STRUC_DEF_CHILD"; + public static final String STRUC_DEF_SINGLE_JSON_OBJECTS = "STRUC_DEF_SINGLE_JSON_OBJECTS"; + public static final String STRUC_DEF_TYPE_SPEC = "STRUC_DEF_TYPE_SPEC"; + public static final String STRUC_DEF_CHOICE_DATA_TYPE = "STRUC_DEF_CHOICE_DATA_TYPE"; + public static final String STRUC_DEF_FURTHER_INFO = "STRUC_DEF_FURTHER_INFO"; + public static final String STRUC_DEF_PRIM_TYPE_VALUE = "STRUC_DEF_PRIM_TYPE_VALUE"; + public static final String STRUC_DEF_PRIM_TYPE_PRESENT = "STRUC_DEF_PRIM_TYPE_PRESENT"; + public static final String STRUC_DEF_PRIM_ELE = "STRUC_DEF_PRIM_ELE"; + public static final String VALUE_SET_CODE_SELEC = "VALUE_SET_CODE_SELEC"; + public static final String VALUE_SET_HAS = "VALUE_SET_HAS"; + public static final String VALUE_SET_CONTAINS = "VALUE_SET_CONTAINS"; + public static final String VALUE_SET_AT_LEAST = "VALUE_SET_AT_LEAST"; + public static final String VALUE_SET_NUMBER_CONCEPTS = "VALUE_SET_NUMBER_CONCEPTS"; + public static final String VALUE_SET_VERSION = "VALUE_SET_VERSION"; + public static final String VALUE_SET_NO_VERSION = "VALUE_SET_NO_VERSION"; + public static final String VALUE_SET_NO_VER = "VALUE_SET_NO_VER"; + public static final String VALUE_SET_THESE_CODES_DEF = "VALUE_SET_THESE_CODES_DEF"; + public static final String VALUE_SET_ALL_CODES_DEF = "VALUE_SET_ALL_CODES_DEF"; + public static final String VALUE_SET_CODES_FROM = "VALUE_SET_CODES_FROM"; + public static final String VALUE_SET_WHERE = "VALUE_SET_WHERE"; + public static final String VALUE_SET_AND = "VALUE_SET_AND"; + public static final String VALUE_SET_DOESNT_EXIST = "VALUE_SET_DOESNT_EXIST"; + public static final String VALUE_SET_SNOMED_ADD = "VALUE_SET_SNOMED_ADD"; + public static final String VALUE_SET_SNOMED = "VALUE_SET_SNOMED"; + public static final String VALUE_SET_LOINCV = "VALUE_SET_LOINCV"; + public static final String CANON_REND_URL = "CANON_REND_URL"; + public static final String CANON_REND_VER = "CANON_REND_VER"; + public static final String CANON_REND_NAME = "CANON_REND_NAME"; + public static final String CANON_REND_TITLE = "CANON_REND_TITLE"; + public static final String CANON_REND_STATUS = "CANON_REND_STATUS"; + public static final String CANON_REND_DEFINITION = "CANON_REND_DEFINITION"; + public static final String CANON_REND_PUBLISHER = "CANON_REND_PUBLISHER"; + public static final String CANON_REND_COMMITTEE = "CANON_REND_COMMITTEE"; + public static final String CANON_REND_COPYRIGHT = "CANON_REND_COPYRIGHT"; + public static final String CANON_REND_MATURITY = "CANON_REND_MATURITY"; + public static final String CANON_REND_SOURCE_RES = "CANON_REND_SOURCE_RES"; + public static final String CANON_REND_XML = "CANON_REND_XML"; + public static final String CANON_REND_JSON = "CANON_REND_JSON"; + public static final String CANON_REND_TURTLE = "CANON_REND_TURTLE"; + public static final String CODE_SYS_CONTENT = "CODE_SYS_CONTENT"; + public static final String CODE_SYS_OID = "CODE_SYS_OID"; + public static final String CODE_SYS_VALUE_SET = "CODE_SYS_VALUE_SET"; + public static final String CODE_SYS_COMPLETE = "CODE_SYS_COMPLETE"; + public static final String CODE_SYS_NOTPRESENT = "CODE_SYS_NOTPRESENT"; + public static final String CODE_SYS_EXAMPLE = "CODE_SYS_EXAMPLE"; + public static final String CODE_SYS_FRAGMENT = "CODE_SYS_FRAGMENT"; + public static final String CODE_SYS_SUPPLEMENT = "CODE_SYS_SUPPLEMENT"; + public static final String CODE_SYS_CODE_NOT_HERE = "CODE_SYS_CODE_NOT_HERE"; + public static final String CODE_SYS_FOR_OID = "CODE_SYS_FOR_OID"; + public static final String CODE_SYS_THE_VALUE_SET = "CODE_SYS_THE_VALUE_SET"; + public static final String STRUC_DEF_NO_SUMMARY = "STRUC_DEF_NO_SUMMARY"; + public static final String STRUC_DEF_ELEMENT = "STRUC_DEF_ELEMENT"; + public static final String STRUC_DEF_STRUCTURES = "STRUC_DEF_STRUCTURES"; + public static final String STRUC_DEF_EXTENSIONS = "STRUC_DEF_EXTENSIONS"; + public static final String STRUC_DEF_SLIC = "STRUC_DEF_SLIC"; + public static final String STRUC_DEF_THIS_REFERS = "STRUC_DEF_THIS_REFERS"; + public static final String STRUC_DEF_REFERS_EXT = "STRUC_DEF_REFERS_EXT"; + public static final String STRUC_DEF_MATURITY = "STRUC_DEF_MATURITY"; + public static final String STRUC_DEF_MODIF = "STRUC_DEF_MODIF"; + public static final String STRUC_DEF_SNOMED_CT = "STRUC_DEF_SNOMED_CT"; + public static final String STRUC_DEF_TERM_BIND = "STRUC_DEF_TERM_BIND"; + public static final String STRUC_DEF_PATH = "STRUC_DEF_PATH"; + public static final String STRUC_DEF_TERM_BINDS = "STRUC_DEF_TERM_BINDS"; + public static final String STRUC_DEF_URI = "STRUC_DEF_URI"; + public static final String STRUC_DEF_MISSING_LINK = "STRUC_DEF_MISSING_LINK"; + public static final String STRUC_DEF_CONSTRAINTS = "STRUC_DEF_CONSTRAINTS"; + public static final String STRUC_DEF_GRADE = "STRUC_DEF_GRADE"; + public static final String STRUC_DEF_NO_MAPPINGS = "STRUC_DEF_NO_MAPPINGS"; + public static final String STRUC_DEF_ALL_MAP_KEY = "STRUC_DEF_ALL_MAP_KEY"; + public static final String STRUC_DEF_PROFILE_BUILDS = "STRUC_DEF_PROFILE_BUILDS"; + public static final String STRUC_DEF_DERIVED_PROFILE = "STRUC_DEF_DERIVED_PROFILE"; + public static final String STRUC_DEF_REFER_PROFILE = "STRUC_DEF_REFER_PROFILE"; + public static final String STRUC_DEF_MOD_ELEMENT = "STRUC_DEF_MOD_ELEMENT"; + public static final String STRUC_DEF_ELE_MUST_SUPP = "STRUC_DEF_ELE_MUST_SUPP"; + public static final String STRUC_DEF_ELE_INCLUDED = "STRUC_DEF_ELE_INCLUDED"; + public static final String STRUC_DEF_AFFECT_CONSTRAINTS = "STRUC_DEF_AFFECT_CONSTRAINTS"; + public static final String STRUC_DEF_CONFORMANCE = "STRUC_DEF_CONFORMANCE"; + public static final String STRUC_DEF_VALUESET_CODE = "STRUC_DEF_VALUESET_CODE"; + public static final String STRUC_DEF_VALUESET = "STRUC_DEF_VALUESET"; + public static final String STRUC_DEF_ID = "STRUC_DEF_ID"; + public static final String STRUC_DEF_PATHS = "STRUC_DEF_PATHS"; + public static final String STRUC_DEF_DETAILS = "STRUC_DEF_DETAILS"; + public static final String VALUE_SET_OID = "VALUE_SET_OID"; + public static final String VALUE_SET_OID_TERM_SYS = "VALUE_SET_OID_TERM_SYS"; + public static final String VALUE_SET_INCLUDED_INTO = "VALUE_SET_INCLUDED_INTO"; + public static final String VALUE_SET_EXCLUDED_FROM = "VALUE_SET_EXCLUDED_FROM"; + public static final String VALUE_SET_USED_ELSEWHERE = "VALUE_SET_USED_ELSEWHERE"; + public static final String TEXT_ICON_REFERENCE = "TEXT_ICON_REFERENCE"; + public static final String TEXT_ICON_PRIMITIVE = "TEXT_ICON_PRIMITIVE"; + public static final String TEXT_ICON_KEY = "TEXT_ICON_KEY"; + public static final String TEXT_ICON_DATATYPE = "TEXT_ICON_DATATYPE"; + public static final String TEXT_ICON_RESOURCE = "TEXT_ICON_RESOURCE"; + public static final String TEXT_ICON_ELEMENT = "TEXT_ICON_ELEMENT"; + public static final String TEXT_ICON_OBJECT_BOX = "TEXT_ICON_OBJECT_BOX"; + public static final String TEXT_ICON_REUSE = "TEXT_ICON_REUSE"; + public static final String TEXT_ICON_EXTENSION = "TEXT_ICON_EXTENSION"; + public static final String TEXT_ICON_CHOICE = "TEXT_ICON_CHOICE"; + public static final String TEXT_ICON_SLICE = "TEXT_ICON_SLICE"; + public static final String TEXT_ICON_SLICE_ITEM = "TEXT_ICON_SLICE_ITEM"; + public static final String TEXT_ICON_FIXED = "TEXT_ICON_FIXED"; + public static final String TEXT_ICON_EXTENSION_SIMPLE = "TEXT_ICON_EXTENSION_SIMPLE"; + public static final String TEXT_ICON_PROFILE = "TEXT_ICON_PROFILE"; + public static final String TEXT_ICON_EXTENSION_COMPLEX = "TEXT_ICON_EXTENSION_COMPLEX"; + public static final String MAP_DEFAULT_COMMENT = "MAP_DEFAULT_COMMENT"; + public static final String VS_ABSTRACT_CODE_HINT = "VS_ABSTRACT_CODE_HINT"; + public static final String REND_ROW_CHANGED_SINCE_WAS = "REND_ROW_CHANGED_SINCE_WAS"; + public static final String TERM_REND_COPY = "TERM_REND_COPY"; + public static final String TEST_PLAN_CASE = "TEST_PLAN_CASE"; + public static final String TEST_PLAN_CASE_SEQ = "TEST_PLAN_CASE_SEQ"; protected String getMessagesSourceFileName() { return "rendering-phrases"; 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 3aca67f43..7bd58dc38 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 @@ -95,22 +95,22 @@ public class HierarchicalTableGenerator { XML, XHTML } - /*!#*/public static final String TEXT_ICON_REFERENCE = "Reference to another Resource"; - /*!#*/public static final String TEXT_ICON_PRIMITIVE = "Primitive Data Type"; - /*!#*/public static final String TEXT_ICON_KEY = "JSON Key Value"; - /*!#*/public static final String TEXT_ICON_DATATYPE = "Data Type"; - /*!#*/public static final String TEXT_ICON_RESOURCE = "Resource"; - /*!#*/public static final String TEXT_ICON_ELEMENT = "Element"; - /*!#*/public static final String TEXT_ICON_OBJECT_BOX = "Object"; - /*!#*/public static final String TEXT_ICON_REUSE = "Reference to another Element"; - /*!#*/public static final String TEXT_ICON_EXTENSION = "Extension"; - /*!#*/public static final String TEXT_ICON_CHOICE = "Choice of Types"; - /*!#*/public static final String TEXT_ICON_SLICE = "Slice Definition"; - /*!#*/public static final String TEXT_ICON_SLICE_ITEM = "Slice Item"; - /*!#*/public static final String TEXT_ICON_FIXED = "Fixed Value"; - /*!#*/public static final String TEXT_ICON_EXTENSION_SIMPLE = "Simple Extension"; - /*!#*/public static final String TEXT_ICON_PROFILE = "Profile"; - /*!#*/public static final String TEXT_ICON_EXTENSION_COMPLEX = "Complex Extension"; + public static final String TEXT_ICON_REFERENCE = "Reference to another Resource"; + public static final String TEXT_ICON_PRIMITIVE = "Primitive Data Type"; + public static final String TEXT_ICON_KEY = "JSON Key Value"; + public static final String TEXT_ICON_DATATYPE = "Data Type"; + public static final String TEXT_ICON_RESOURCE = "Resource"; + public static final String TEXT_ICON_ELEMENT = "Element"; + public static final String TEXT_ICON_OBJECT_BOX = "Object"; + public static final String TEXT_ICON_REUSE = "Reference to another Element"; + public static final String TEXT_ICON_EXTENSION = "Extension"; + public static final String TEXT_ICON_CHOICE = "Choice of Types"; + public static final String TEXT_ICON_SLICE = "Slice Definition"; + public static final String TEXT_ICON_SLICE_ITEM = "Slice Item"; + public static final String TEXT_ICON_FIXED = "Fixed Value"; + public static final String TEXT_ICON_EXTENSION_SIMPLE = "Simple Extension"; + public static final String TEXT_ICON_PROFILE = "Profile"; + public static final String TEXT_ICON_EXTENSION_COMPLEX = "Complex Extension"; public static final int NEW_REGULAR = 0; public static final int CONTINUE_REGULAR = 1; diff --git a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties index 8296f40d2..ffb4bc525 100644 --- a/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties +++ b/org.hl7.fhir.utilities/src/main/resources/rendering-phrases.properties @@ -502,6 +502,9 @@ REND_ADDED = Added: REND_CHANGED = Changed: REND_REMOVED = Removed: REND_ROW_SINCE = This row of content has been added since {0} +REND_ROW_CHANGED_SINCE = This row of content has been changed since {0} +REND_ROW_CHANGED_SINCE_WAS = This row of content has been changed since {0} (was ''{1}'') +REND_ROW_REMOVED_SINCE = This content has been removed since {0} REQ_ACTOR = These requirements apply to the actor REQ_FOLLOWING_ACTOR = These requirements apply to the following actors: REQ_DERIVE = These requirements derive from @@ -908,3 +911,156 @@ CAPABILITY_INT_SUMM = Interaction summary CAPABILITY_SEARCH_PARS = Search Parameters CAPABILITY_COMB_SEARCH_PARS = Combined Search Parameters CODE_SYS_IN_A_HIERARCHY = in a {0} hierarchy +CODE_SYS_REPLACED_BY = (replaced by +DATA_REND_ONGOING = (ongoing) +EXAMPLE_SCEN_UNABLE_TO_FIND = Unable to find referenced version {0} within instance {1} +EXAMPLE_SCEN_STEP_SCEN = Step {0} - See scenario\n {1} +DATA_REND_DETAILS_STATED = (Details: {0} code {1} {2} {3} ', stated as ' {4} {5} +DATA_REND_VERSION = (version = {0} {1} +DATA_REND_BASE64 = (base64 data - {0} bytes) +DATA_REND_CODES = Codes: +DATA_REND_GLN = Global Location Number +DATA_REND_DETAILS = (Details: {0} code +DATA_REND_PER = per +DATA_REND_EXCEPTION = Exception +DIAG_REP_REND_PER = Performer +DIAG_REP_REND_IDENTIFIER = Identifier +DIAG_REP_REND_REQUEST = Request +PROF_DRIV_GEN_NARR = Generated Narrative: {0} {1} +PROF_DRIV_ERR_GEN_NARR = Error Generating Narrative for +QUEST_UNKNOWN_MODE = Unknown QuestionnaireResponse Renderer Mode +QUEST_ANSWER = Answer +QUEST_RESP_ROOT = QuestionnaireResponseRoot +QUEST_RESP = QuestionnaireResponse +QUEST_QUESTION = Questionnaire: +QUEST_NONE_SPEC = None specified +QUEST_GROUP = Group +QUEST_ITEM = Item +QUEST_TRY_QUEST = Try this QuestionnaireResponse out: +QUEST_DISPLAY_CAT = Display Category +QUEST_NOT_DONE = Not done yet +RES_REND_VER = (version +RES_REND_VERSION = Version +RES_REND_UPDATED = Updated +RES_REND_LANGUAGE = (Language +RES_REND_SPEC_RULES = Special rules apply: +RES_REND_INFO_SOURCE = Information Source: +RES_REND_PROFILE = Profile +RES_REND_SECURITY_LABEL = Security Label +SEARCH_PAR_REND_RES = Resource +SEARCH_PAR_REND_TARGET = Target Resources +STRUC_DEF_REND_UNABLE_RES = Unable to resolve StructureDefinition {0} resolving content reference {1} +STRUC_DEF_CANT_FIND = getElementByName: can't find {0} in {1} from {2} +STRUC_DEF_ALL_SLICES = :All Slices +STRUC_DEF_ELE_AFFECTED = This element has or is affected by constraints ( {0} {1} +STRUC_DEF_STAND_STATUS = Standards Status = +STRUC_DEF_BLACK = black +STRUC_DEF_ABSTRACT = This is an abstract +STRUC_DEF_CHILD = Child +STRUC_DEF_SINGLE_JSON_OBJECTS = JSON: Represented as a single JSON Object with named properties using the value of the {0} child as the key +STRUC_DEF_TYPE_SPEC = Type Specifier +STRUC_DEF_CHOICE_DATA_TYPE = Choice of Data Types +STRUC_DEF_FURTHER_INFO = for further information about how to use [x] +STRUC_DEF_PRIM_TYPE_VALUE = This primitive type must have a value (the value must be present, and cannot be replaced by an extension) +STRUC_DEF_PRIM_TYPE_PRESENT = This primitive type may be present, or absent if replaced by one of the following extensions: +STRUC_DEF_PRIM_ELE = This primitive element may be present, or absent, or replaced by an extension +VALUE_SET_CODE_SELEC = This value set cannot be fully expanded, but a selection ( {0} codes) of the whole set of codes is shown here. +VALUE_SET_HAS = This value set has +VALUE_SET_CONTAINS = This value set contains +VALUE_SET_AT_LEAST = at least +VALUE_SET_NUMBER_CONCEPTS = This value set expansion contains {0} concepts. +VALUE_SET_VERSION = version +VALUE_SET_NO_VERSION = (no version) ( +VALUE_SET_NO_VER = (no version) +VALUE_SET_ALL_CODES_DEF = all codes defined in +VALUE_SET_THESE_CODES_DEF = these codes as defined in +VALUE_SET_CODES_FROM = codes from +VALUE_SET_WHERE = where +VALUE_SET_AND = and +VALUE_SET_DOESNT_EXIST = doesn't exist +VALUE_SET_SNOMED_ADD = SNOMED CT {0} edition {1} +VALUE_SET_SNOMED = SNOMED CT {0} edition +VALUE_SET_LOINCV = Loinc v +CANON_REND_URL = Defining URL +CANON_REND_VER = Version +CANON_REND_NAME = Name +CANON_REND_TITLE = Title +CANON_REND_STATUS = Status +CANON_REND_DEFINITION = Definition +CANON_REND_PUBLISHER = Publisher +CANON_REND_COMMITTEE = Committee +CANON_REND_COPYRIGHT = Copyright +CANON_REND_MATURITY = Maturity +CANON_REND_SOURCE_RES = Source Resource +CANON_REND_XML = XML +CANON_REND_JSON = JSON +CANON_REND_TURTLE = Turtle +CODE_SYS_CONTENT = Content +CODE_SYS_OID = OID +CODE_SYS_VALUE_SET = Value Set +CODE_SYS_COMPLETE = All the concepts defined by the code system are included in the code system resource +CODE_SYS_NOTPRESENT = None of the concepts defined by the code system are included in the code system resource +CODE_SYS_EXAMPLE = A few representative concepts are included in the code system resource +CODE_SYS_FRAGMENT = A subset of the code system concepts are included in the code system resource +CODE_SYS_SUPPLEMENT = This code system resource is a supplement to +CODE_SYS_CODE_NOT_HERE = This CodeSystem is not used here; it may be used elsewhere (e.g. specifications and/or implementations that use this content) +CODE_SYS_FOR_OID = for OID based terminology systems +CODE_SYS_THE_VALUE_SET = is the value set for all codes in this code system +STRUC_DEF_NO_SUMMARY = No Summary, as this profile has no differential +STRUC_DEF_ELEMENT = element +STRUC_DEF_STRUCTURES = Structures +STRUC_DEF_EXTENSIONS = Extensions +STRUC_DEF_SLIC = Slices +STRUC_DEF_THIS_REFERS = This structure refers to these other structures +STRUC_DEF_REFERS_EXT = This structure refers to these extensions +STRUC_DEF_MATURITY = Maturity +STRUC_DEF_MODIF = Modifier +STRUC_DEF_SNOMED_CT = SNOMED CT +STRUC_DEF_TERM_BIND = Terminology Bindings (Differential) +STRUC_DEF_PATH = Path +STRUC_DEF_TERM_BINDS = Terminology Bindings +STRUC_DEF_URI = URI +STRUC_DEF_MISSING_LINK = missing link +STRUC_DEF_CONSTRAINTS = Constraints +STRUC_DEF_GRADE = Grade +STRUC_DEF_NO_MAPPINGS = No Mappings +STRUC_DEF_ALL_MAP_KEY = All Mappings are Empty +STRUC_DEF_PROFILE_BUILDS = This profile builds on +STRUC_DEF_DERIVED_PROFILE = In this IG, the following structures are derived from this profile: +STRUC_DEF_REFER_PROFILE = In this IG, the following structures refer to this profile: +STRUC_DEF_MOD_ELEMENT = This element is a modifier element +STRUC_DEF_ELE_MUST_SUPP = This element must be supported +STRUC_DEF_ELE_INCLUDED = This element is included in summaries +STRUC_DEF_AFFECT_CONSTRAINTS = "This element has or is affected by constraints ( +STRUC_DEF_CONFORMANCE = Conformance +STRUC_DEF_VALUESET_CODE = ValueSet / Code +STRUC_DEF_VALUESET = ValueSet +STRUC_DEF_ID = Id +STRUC_DEF_PATHS = Path(s) +STRUC_DEF_DETAILS = Details +VALUE_SET_OID = OID +VALUE_SET_OID_TERM_SYS = for OID based terminology systems +VALUE_SET_INCLUDED_INTO = Included into +VALUE_SET_EXCLUDED_FROM = Excluded from +VALUE_SET_USED_ELSEWHERE = This value set is not used here; it may be used elsewhere (e.g. specifications and/or implementations that use this content) +TEXT_ICON_REFERENCE = Reference to another Resource +TEXT_ICON_PRIMITIVE = Primitive Data Type +TEXT_ICON_KEY = JSON Key Value +TEXT_ICON_DATATYPE = Data Type +TEXT_ICON_RESOURCE = Resource +TEXT_ICON_ELEMENT = Element +TEXT_ICON_OBJECT_BOX = Object +TEXT_ICON_REUSE = Reference to another Element +TEXT_ICON_EXTENSION = Extension +TEXT_ICON_CHOICE = Choice of Types +TEXT_ICON_SLICE = Slice Definition +TEXT_ICON_SLICE_ITEM = Slice Item +TEXT_ICON_FIXED = Fixed Value +TEXT_ICON_EXTENSION_SIMPLE = Simple Extension +TEXT_ICON_PROFILE = Profile +TEXT_ICON_EXTENSION_COMPLEX = Complex Extension +MAP_DEFAULT_COMMENT = This element was not defined prior to R5 +VS_ABSTRACT_CODE_HINT = This code is not selectable ('Abstract') +TERM_REND_COPY = Copy {0} Format to clipboard +TEST_PLAN_CASE = Test Case +TEST_PLAN_CASE_SEQ = Test Case - Sequence {0} From 21e7e70a990f958c26476eeaef909aaf22dcd786 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sun, 19 May 2024 09:36:20 -0500 Subject: [PATCH 13/14] put jurisdiction in npm package.json --- .../main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java index cc682f006..9ba2fab0f 100644 --- a/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java +++ b/org.hl7.fhir.r5/src/main/java/org/hl7/fhir/r5/utils/NPMPackageGenerator.java @@ -249,6 +249,10 @@ public class NPMPackageGenerator { npm.add("directories", dir); dir.add("lib", "package"); dir.add("example", "example"); + if (ig.hasJurisdiction() && ig.getJurisdiction().size() == 1 && ig.getJurisdictionFirstRep().getCoding().size() == 1) { + Coding c = ig.getJurisdictionFirstRep().getCodingFirstRep(); + npm.add("jurisdiction", c.getSystem()+"#"+c.getCode()); + } String json = JsonParser.compose(npm, true); try { addFile(Category.RESOURCE, "package.json", json.getBytes("UTF-8")); From bf4b029b4be0e4873cc9a06da5dd3fed9e2b7067 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Sun, 19 May 2024 10:45:04 -0500 Subject: [PATCH 14/14] compile fixes --- .../conv30_50/datatypes30_50/ElementDefinition30_50.java | 2 +- .../main/java/org/hl7/fhir/utilities/i18n/I18nBase.java | 6 +++++- .../org/hl7/fhir/utilities/i18n/RenderingI18nContext.java | 7 +++++++ 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java index 256ff156e..3cf1d0ae9 100644 --- a/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java +++ b/org.hl7.fhir.convertors/src/main/java/org/hl7/fhir/convertors/conv30_50/datatypes30_50/ElementDefinition30_50.java @@ -730,7 +730,7 @@ public class ElementDefinition30_50 { Extension tgt = new Extension(); ConversionContext30_50.INSTANCE.getVersionConvertor_30_50().copyElement(src, tgt); if (src.hasPurpose()) { - tgt.addExtension(new Extension("purpose", new CodeType(src.getPurposeElement().primitiveValue()))); + tgt.addExtension(new Extension("purpose", new org.hl7.fhir.dstu3.model.CodeType(src.getPurposeElement().primitiveValue()))); } if (src.hasValueSet()) { tgt.addExtension(new Extension("valueSet", Uri30_50.convertCanonical(src.getValueSetElement()))); diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nBase.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nBase.java index 2aaeeaac8..152b10db8 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nBase.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/I18nBase.java @@ -66,7 +66,7 @@ public abstract class I18nBase { if (!messageKeyExistsForLocale(message)) { if (!message.contains(" ")) { if (warnAboutMissingMessages && (hasArgs || !message.contains(" "))) { - System.out.println("Attempting to localize message " + message + ", but no such equivalent message exists for" + + System.out.println("Attempting to localize "+typeOfString()+" " + message + ", but no such equivalent message exists for" + " the locale " + getLocale()); } } @@ -74,6 +74,10 @@ public abstract class I18nBase { return messageKeyExistsForLocale(message); } + protected String typeOfString() { + return "message"; + } + protected boolean messageKeyExistsForLocale(String message) { return messages.containsKey(message); } diff --git a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java index ae2302b76..fa948f400 100644 --- a/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java +++ b/org.hl7.fhir.utilities/src/main/java/org/hl7/fhir/utilities/i18n/RenderingI18nContext.java @@ -1065,4 +1065,11 @@ public class RenderingI18nContext extends I18nBase { protected String getMessagesSourceFileName() { return "rendering-phrases"; } + +@Override + protected String typeOfString() { + return "phrase"; + } + + }