From db8f28ea5188e3c4e8a6c7f79679f26537e90f76 Mon Sep 17 00:00:00 2001 From: Grahame Grieve Date: Mon, 14 Sep 2020 18:09:40 +1000 Subject: [PATCH] fix issue rendering translations in value sets --- .../fhir/r5/renderers/CodeSystemRenderer.java | 2 +- .../r5/renderers/TerminologyRenderer.java | 25 ++- .../fhir/r5/renderers/ValueSetRenderer.java | 181 ++++++++++++++---- 3 files changed, 167 insertions(+), 41 deletions(-) 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 065bdacba..c6abb1b4a 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 @@ -166,7 +166,7 @@ public class CodeSystemRenderer extends TerminologyRenderer { hierarchy = hierarchy || csNav.isRestructure(); List langs = new ArrayList<>(); - addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, true, commentS, version, deprecated, properties), maps); + addMapHeaders(addTableHeaderRowStandard(t, hierarchy, display, true, commentS, version, deprecated, properties, null, false), maps); for (ConceptDefinitionComponent c : csNav.getConcepts(null)) { hasExtensions = addDefineRowToTable(t, c, 0, hierarchy, display, commentS, version, deprecated, maps, cs.getUrl(), cs, properties, csNav, langs) || hasExtensions; } 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 46143644a..d90d652ba 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 @@ -201,21 +201,27 @@ public abstract class TerminologyRenderer extends ResourceRenderer { return null; } - protected XhtmlNode addTableHeaderRowStandard(XhtmlNode t, boolean hasHierarchy, boolean hasDisplay, boolean definitions, boolean comments, boolean version, boolean deprecated, List properties) { + protected XhtmlNode addTableHeaderRowStandard(XhtmlNode t, boolean hasHierarchy, boolean hasDisplay, boolean definitions, boolean comments, boolean version, boolean deprecated, List properties, List langs, boolean doLangs) { XhtmlNode tr = t.tr(); - if (hasHierarchy) + if (hasHierarchy) { tr.td().b().tx("Lvl"); + } tr.td().attribute("style", "white-space:nowrap").b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Code", getContext().getLang())); - if (hasDisplay) + if (hasDisplay) { tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Display", getContext().getLang())); - if (definitions) + } + if (definitions) { tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Definition", getContext().getLang())); - if (deprecated) + } + if (deprecated) { tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Deprecated", getContext().getLang())); - if (comments) + } + if (comments) { tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Comments", getContext().getLang())); - if (version) + } + if (version) { tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", "Version", getContext().getLang())); + } if (properties != null) { for (PropertyComponent pc : properties) { String display = ToolingExtensions.getPresentation(pc, pc.getCodeElement()); @@ -228,6 +234,11 @@ public abstract class TerminologyRenderer extends ResourceRenderer { tr.td().b().tx(getContext().getWorker().translator().translate("xhtml-gen-cs", display, getContext().getLang())); } } + if (doLangs) { + for (String lang : langs) { + tr.td().b().addText(describeLang(lang)); + } + } return tr; } 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 e435b4707..6a6969523 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 @@ -1,5 +1,7 @@ package org.hl7.fhir.r5.renderers; +import java.io.BufferedWriter; +import java.io.FileWriter; import java.io.IOException; import java.text.ParseException; import java.text.SimpleDateFormat; @@ -63,6 +65,8 @@ public class ValueSetRenderer extends TerminologyRenderer { private static final String ABSTRACT_CODE_HINT = "This code is not selectable ('Abstract')"; + private static final int MAX_LANGS_IN_LINE = 5; + private List renderingMaps = new ArrayList(); public boolean render(XhtmlNode x, Resource dr) throws FHIRFormatError, DefinitionException, IOException { @@ -222,25 +226,44 @@ public class ValueSetRenderer extends TerminologyRenderer { tr.td().attribute("style", "white-space:nowrap").b().tx("Code"); if (doSystem) tr.td().b().tx("System"); - tr.td().b().tx("Display"); - if (doDefinition) + XhtmlNode tdDisp = tr.td(); + tdDisp.b().tx("Display"); + boolean doLangs = false; + for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { + scanForLangs(c, langs); + } + if (doDefinition) { tr.td().b().tx("Definition"); + } else { + // if we're not doing definitions and we don't have too many languages, we'll do them in line + if (langs.size() < MAX_LANGS_IN_LINE) { + doLangs = true; + if (vs.hasLanguage()) { + tdDisp.tx(" - "+describeLang(vs.getLanguage())); + } + for (String lang : langs) { + tr.td().b().addText(describeLang(lang)); + } + } + } + addMapHeaders(tr, maps); for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { - addExpansionRowToTable(t, c, 0, doLevel, doSystem, doDefinition, maps, allCS, langs); + addExpansionRowToTable(t, c, 0, doLevel, doSystem, doDefinition, maps, allCS, langs, doLangs); } // now, build observed languages - if (langs.size() > 0) { + if (!doLangs && langs.size() > 0) { Collections.sort(langs); x.para().b().tx("Additional Language Displays"); t = x.table( "codes"); tr = t.tr(); - tr.td().b().tx("Code"); - for (String lang : langs) - tr.td().b().addText(describeLang(lang)); + tdDisp.b().tx("Code"); + for (String lang : langs) { + tdDisp.b().addText(describeLang(lang)); + } for (ValueSetExpansionContainsComponent c : vs.getExpansion().getContains()) { addLanguageRow(c, t, langs); } @@ -505,20 +528,33 @@ public class ValueSetRenderer extends TerminologyRenderer { private void addLanguageRow(ValueSetExpansionContainsComponent c, XhtmlNode t, List langs) { XhtmlNode tr = t.tr(); tr.td().addText(c.getCode()); + addLangaugesToRow(c, langs, tr); + for (ValueSetExpansionContainsComponent cc : c.getContains()) { + addLanguageRow(cc, t, langs); + } + } + + public void addLangaugesToRow(ValueSetExpansionContainsComponent c, List langs, XhtmlNode tr) { for (String lang : langs) { String d = null; for (Extension ext : c.getExtension()) { if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { String l = ToolingExtensions.readStringExtension(ext, "lang"); - if (lang.equals(l)) + if (lang.equals(l)) { d = ToolingExtensions.readStringExtension(ext, "content"); + } + } + } + if (d == null) { + for (ConceptReferenceDesignationComponent dd : c.getDesignation()) { + String l = dd.getLanguage(); + if (lang.equals(l)) { + d = dd.getValue(); + } } } tr.td().addText(d == null ? "" : d); } - for (ValueSetExpansionContainsComponent cc : c.getContains()) { - addLanguageRow(cc, t, langs); - } } @@ -561,7 +597,27 @@ public class ValueSetRenderer extends TerminologyRenderer { return ref.replace("\\", "/"); } - private void addExpansionRowToTable(XhtmlNode t, ValueSetExpansionContainsComponent c, int i, boolean doLevel, boolean doSystem, boolean doDefinition, List maps, CodeSystem allCS, List langs) { + private void scanForLangs(ValueSetExpansionContainsComponent c, List langs) { + for (Extension ext : c.getExtension()) { + if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { + String lang = ToolingExtensions.readStringExtension(ext, "lang"); + if (!Utilities.noString(lang) && !langs.contains(lang)) { + langs.add(lang); + } + } + } + for (ConceptReferenceDesignationComponent d : c.getDesignation()) { + String lang = d.getLanguage(); + if (!Utilities.noString(lang) && !langs.contains(lang)) { + langs.add(lang); + } + } + for (ValueSetExpansionContainsComponent cc : c.getContains()) { + scanForLangs(cc, langs); + } + } + + private void addExpansionRowToTable(XhtmlNode t, ValueSetExpansionContainsComponent c, int i, boolean doLevel, boolean doSystem, boolean doDefinition, List maps, CodeSystem allCS, List langs, boolean doLangs) { XhtmlNode tr = t.tr(); XhtmlNode td = tr.td(); @@ -606,15 +662,24 @@ public class ValueSetRenderer extends TerminologyRenderer { td.i().tx("("+mapping.comp.getComment()+")"); } } - for (Extension ext : c.getExtension()) { - if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { - String lang = ToolingExtensions.readStringExtension(ext, "lang"); - if (!Utilities.noString(lang) && !langs.contains(lang)) - langs.add(lang); - } - } + addLangaugesToRow(c, langs, tr); +// ! +// for (Extension ext : c.getExtension()) { +// if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { +// String lang = ToolingExtensions.readStringExtension(ext, "lang"); +// if (!Utilities.noString(lang) && !langs.contains(lang)) { +// langs.add(lang); +// } +// } +// } +// for (ConceptReferenceDesignationComponent d : c.getDesignation()) { +// String lang = d.getLanguage(); +// if (!Utilities.noString(lang) && !langs.contains(lang)) { +// langs.add(lang); +// } +// } for (ValueSetExpansionContainsComponent cc : c.getContains()) { - addExpansionRowToTable(t, cc, i+1, doLevel, doSystem, doDefinition, maps, allCS, langs); + addExpansionRowToTable(t, cc, i+1, doLevel, doSystem, doDefinition, maps, allCS, langs, doLangs); } } @@ -677,7 +742,14 @@ public class ValueSetRenderer extends TerminologyRenderer { private boolean generateComposition(XhtmlNode x, ValueSet vs, boolean header, List maps) throws FHIRException, IOException { boolean hasExtensions = false; List langs = new ArrayList(); - + for (ConceptSetComponent inc : vs.getCompose().getInclude()) { + scanForLangs(inc, langs); + } + for (ConceptSetComponent inc : vs.getCompose().getExclude()) { + scanForLangs(inc, langs); + } + boolean doLangs = langs.size() < MAX_LANGS_IN_LINE; + if (header) { XhtmlNode h = x.h2(); h.addText(vs.present()); @@ -686,27 +758,27 @@ public class ValueSetRenderer extends TerminologyRenderer { generateCopyright(x, vs); } if (vs.getCompose().getInclude().size() == 1 && vs.getCompose().getExclude().size() == 0) { - hasExtensions = genInclude(x.ul(), vs.getCompose().getInclude().get(0), "Include", langs, maps) || hasExtensions; + hasExtensions = genInclude(x.ul(), vs.getCompose().getInclude().get(0), "Include", langs, doLangs, maps) || hasExtensions; } else { XhtmlNode p = x.para(); p.tx("This value set includes codes based on the following rules:"); XhtmlNode ul = x.ul(); for (ConceptSetComponent inc : vs.getCompose().getInclude()) { - hasExtensions = genInclude(ul, inc, "Include", langs, maps) || hasExtensions; + hasExtensions = genInclude(ul, inc, "Include", langs, doLangs, maps) || hasExtensions; } if (vs.getCompose().hasExclude()) { p = x.para(); p.tx("This value set excludes codes based on the following rules:"); ul = x.ul(); for (ConceptSetComponent exc : vs.getCompose().getExclude()) { - hasExtensions = genInclude(ul, exc, "Exclude", langs, maps) || hasExtensions; + hasExtensions = genInclude(ul, exc, "Exclude", langs, doLangs, maps) || hasExtensions; } } } // now, build observed languages - if (langs.size() > 0) { + if (!doLangs && langs.size() > 0) { Collections.sort(langs); x.para().b().tx("Additional Language Displays"); XhtmlNode t = x.table( "codes"); @@ -724,7 +796,26 @@ public class ValueSetRenderer extends TerminologyRenderer { return hasExtensions; } - private boolean genInclude(XhtmlNode ul, ConceptSetComponent inc, String type, List langs, List maps) throws FHIRException, IOException { + private void scanForLangs(ConceptSetComponent inc, List langs) { + for (ConceptReferenceComponent cc : inc.getConcept()) { + for (Extension ext : cc.getExtension()) { + if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { + String lang = ToolingExtensions.readStringExtension(ext, "lang"); + if (!Utilities.noString(lang) && !langs.contains(lang)) { + langs.add(lang); + } + } + } + for (ConceptReferenceDesignationComponent d : cc.getDesignation()) { + String lang = d.getLanguage(); + if (!Utilities.noString(lang) && !langs.contains(lang)) { + langs.add(lang); + } + } + } + } + + private boolean genInclude(XhtmlNode ul, ConceptSetComponent inc, String type, List langs, boolean doLangs, List maps) throws FHIRException, IOException { boolean hasExtensions = false; XhtmlNode li; li = ul.li(); @@ -755,7 +846,7 @@ public class ValueSetRenderer extends TerminologyRenderer { } if (hasComments || hasDefinition) hasExtensions = true; - addMapHeaders(addTableHeaderRowStandard(t, false, true, hasDefinition, hasComments, false, false, null), maps); + addMapHeaders(addTableHeaderRowStandard(t, false, true, hasDefinition, hasComments, false, false, null, langs, doLangs), maps); for (ConceptReferenceComponent c : inc.getConcept()) { XhtmlNode tr = t.tr(); XhtmlNode td = tr.td(); @@ -768,18 +859,19 @@ public class ValueSetRenderer extends TerminologyRenderer { else if (cc != null && !Utilities.noString(cc.getDisplay())) td.addText(cc.getDisplay()); - td = tr.td(); - if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION)) + if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_DEFINITION)) { + td = tr.td(); smartAddText(td, ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_DEFINITION)); - else if (cc != null && !Utilities.noString(cc.getDefinition())) + } else if (cc != null && !Utilities.noString(cc.getDefinition())) { + td = tr.td(); smartAddText(td, cc.getDefinition()); + } if (ExtensionHelper.hasExtension(c, ToolingExtensions.EXT_VS_COMMENT)) { smartAddText(tr.td(), "Note: "+ToolingExtensions.readStringExtension(c, ToolingExtensions.EXT_VS_COMMENT)); } - for (ConceptReferenceDesignationComponent cd : c.getDesignation()) { - if (cd.hasLanguage() && !langs.contains(cd.getLanguage())) - langs.add(cd.getLanguage()); + if (doLangs) { + addLangaugesToRow(c, langs, tr); } } } @@ -852,6 +944,29 @@ public class ValueSetRenderer extends TerminologyRenderer { return hasExtensions; } + public void addLangaugesToRow(ConceptReferenceComponent c, List langs, XhtmlNode tr) { + for (String lang : langs) { + String d = null; + for (Extension ext : c.getExtension()) { + if (ToolingExtensions.EXT_TRANSLATION.equals(ext.getUrl())) { + String l = ToolingExtensions.readStringExtension(ext, "lang"); + if (lang.equals(l)) { + d = ToolingExtensions.readStringExtension(ext, "content"); + } + } + } + if (d == null) { + for (ConceptReferenceDesignationComponent dd : c.getDesignation()) { + String l = dd.getLanguage(); + if (lang.equals(l)) { + d = dd.getValue(); + } + } + } + tr.td().addText(d == null ? "" : d); + } + } + private Map getConceptsForCodes(CodeSystem e, ConceptSetComponent inc) { if (e == null) {