001package org.hl7.fhir.r5.elementmodel; 002 003import java.util.ArrayList; 004import java.util.Arrays; 005import java.util.HashSet; 006import java.util.List; 007import java.util.Set; 008 009import org.checkerframework.checker.units.qual.cd; 010import org.hl7.fhir.r5.context.ContextUtilities; 011import org.hl7.fhir.r5.context.IWorkerContext; 012import org.hl7.fhir.r5.model.Base; 013import org.hl7.fhir.r5.model.CodeSystem; 014import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionComponent; 015import org.hl7.fhir.r5.model.CodeSystem.ConceptDefinitionDesignationComponent; 016import org.hl7.fhir.r5.model.CodeSystem.ConceptPropertyComponent; 017import org.hl7.fhir.r5.model.ContactDetail; 018import org.hl7.fhir.r5.model.DataType; 019import org.hl7.fhir.r5.model.ElementDefinition; 020import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionBindingAdditionalComponent; 021import org.hl7.fhir.r5.model.ElementDefinition.ElementDefinitionConstraintComponent; 022import org.hl7.fhir.r5.model.Extension; 023import org.hl7.fhir.r5.model.MarkdownType; 024import org.hl7.fhir.r5.model.Property; 025import org.hl7.fhir.r5.model.Resource; 026import org.hl7.fhir.r5.model.StringType; 027import org.hl7.fhir.r5.model.StructureDefinition; 028import org.hl7.fhir.r5.terminologies.CodeSystemUtilities; 029import org.hl7.fhir.r5.utils.ToolingExtensions; 030import org.hl7.fhir.utilities.TextFile; 031import org.hl7.fhir.utilities.Utilities; 032import org.hl7.fhir.utilities.i18n.AcceptLanguageHeader; 033import org.hl7.fhir.utilities.i18n.AcceptLanguageHeader.LanguagePreference; 034import org.hl7.fhir.utilities.i18n.LanguageFileProducer; 035import org.hl7.fhir.utilities.i18n.LanguageFileProducer.LanguageProducerLanguageSession; 036import org.hl7.fhir.utilities.i18n.LanguageFileProducer.TextUnit; 037import org.hl7.fhir.utilities.i18n.LanguageFileProducer.TranslationUnit; 038import org.hl7.fhir.utilities.validation.ValidationMessage; 039import org.hl7.fhir.utilities.validation.ValidationMessage.IssueSeverity; 040import org.hl7.fhir.utilities.validation.ValidationMessage.IssueType; 041import org.hl7.fhir.utilities.validation.ValidationMessage.Source; 042 043/** 044 * in here: 045 * * generateTranslations 046 * * importFromTranslations 047 * * stripTranslations 048 * * switchLanguage 049 * 050 * in the validator 051 * 052 * @author grahamegrieve 053 * generateTranslations = -langTransform export -src {src} -tgt {tgt} -dest {dest} 054 * importFromTranslations = -langTransform import -src {src} -tgt {tgt} -dest {dest} 055 */ 056public class LanguageUtils { 057 058 public static final List<String> TRANSLATION_SUPPLEMENT_RESOURCE_TYPES = Arrays.asList("CodeSystem", "StructureDefinition", "Questionnaire"); 059 060 private static final String ORPHAN_TRANSLATIONS_NAME = "translations.orphans"; 061 062 private static final String SUPPLEMENT_SOURCE_RESOURCE = "translations.supplemented"; 063 private static final String SUPPLEMENT_SOURCE_TRANSLATIONS = "translations.source-list"; 064 065 IWorkerContext context; 066 private List<String> crlist; 067 068 069 public LanguageUtils(IWorkerContext context) { 070 super(); 071 this.context = context; 072 } 073 074 public void generateTranslations(Element resource, LanguageProducerLanguageSession session) { 075 translate(null, resource, session); 076 } 077 078 079 private void translate(Element parent, Element element, LanguageProducerLanguageSession langSession) { 080 if (element.isPrimitive() && isTranslatable(element)) { 081 String base = element.primitiveValue(); 082 if (base != null) { 083 String translation = getSpecialTranslation(parent, element, langSession.getTargetLang()); 084 if (translation == null) { 085 translation = element.getTranslation(langSession.getTargetLang()); 086 } 087 langSession.entry(new TextUnit(pathForElement(element), contextForElement(element), base, translation)); 088 } 089 } 090 for (Element c: element.getChildren()) { 091 if (!c.getName().equals("designation")) { 092 translate(element, c, langSession); 093 } 094 } 095 } 096 097 private String contextForElement(Element element) { 098 throw new Error("Not done yet"); 099 } 100 101 private String getSpecialTranslation(Element parent, Element element, String targetLang) { 102 if (parent == null) { 103 return null; 104 } 105 if (Utilities.existsInList(pathForElement(parent), "CodeSystem.concept", "CodeSystem.concept.concept") && "CodeSystem.concept.display".equals(pathForElement(element))) { 106 return getDesignationTranslation(parent, targetLang); 107 } 108 if (Utilities.existsInList(pathForElement(parent), "ValueSet.compose.include.concept") && "ValueSet.compose.include.concept.display".equals(pathForElement(element))) { 109 return getDesignationTranslation(parent, targetLang); 110 } 111 if (Utilities.existsInList(pathForElement(parent), "ValueSet.expansion.contains", "ValueSet.expansion.contains.contains") && "ValueSet.expansion.contains.display".equals(pathForElement(element))) { 112 return getDesignationTranslation(parent, targetLang); 113 } 114 return null; 115 } 116 117 private String getDesignationTranslation(Element parent, String targetLang) { 118 for (Element e : parent.getChildren("designation")) { 119 String lang = e.getNamedChildValue("language"); 120 if (langsMatch(targetLang, lang)) { 121 return e.getNamedChildValue("value"); 122 } 123 } 124 return null; 125 } 126 127 private boolean isTranslatable(Element element) { 128 return element.getProperty().isTranslatable(); 129 } 130 131 private String pathForElement(Element element) { 132 String bp = element.getBasePath(); 133 return pathForElement(bp, element.getProperty().getStructure().getType()); 134 } 135 136 private String pathForElement(String path, String type) { 137 // special case support for metadata elements prior to R5: 138 if (crlist == null) { 139 crlist = new ContextUtilities(context).getCanonicalResourceNames(); 140 } 141 if (crlist.contains(type)) { 142 String fp = path.replace(type+".", "CanonicalResource."); 143 if (Utilities.existsInList(fp, 144 "CanonicalResource.url", "CanonicalResource.identifier", "CanonicalResource.version", "CanonicalResource.name", 145 "CanonicalResource.title", "CanonicalResource.status", "CanonicalResource.experimental", "CanonicalResource.date", 146 "CanonicalResource.publisher", "CanonicalResource.contact", "CanonicalResource.description", "CanonicalResource.useContext", 147 "CanonicalResource.jurisdiction")) { 148 return fp; 149 } 150 } 151 return path; 152 } 153 154 155 public int importFromTranslations(Element resource, List<TranslationUnit> translations) { 156 return importFromTranslations(null, resource, translations, new HashSet<>()); 157 } 158 159 public int importFromTranslations(Element resource, List<TranslationUnit> translations, List<ValidationMessage> messages) { 160 Set<TranslationUnit> usedUnits = new HashSet<>(); 161 int r = 0; 162 if (resource.fhirType().equals("StructureDefinition")) { 163 r = importFromTranslationsForSD(null, resource, translations, usedUnits); 164 } else { 165 r = importFromTranslations(null, resource, translations, usedUnits); 166 } 167 for (TranslationUnit t : translations) { 168 if (!usedUnits.contains(t)) { 169 messages.add(new ValidationMessage(Source.Publisher, IssueType.INFORMATIONAL, t.getId(), "Unused '"+t.getLanguage()+"' translation '"+t.getSrcText()+"' -> '"+t.getTgtText()+"'", IssueSeverity.INFORMATION)); 170 } 171 } 172 return r; 173 } 174 175 public int importFromTranslations(Resource resource, List<TranslationUnit> translations, List<ValidationMessage> messages) { 176 Set<TranslationUnit> usedUnits = new HashSet<>(); 177 int r = 0; 178 if (resource.fhirType().equals("StructureDefinition")) { 179 // todo... r = importFromTranslationsForSD(null, resource, translations, usedUnits); 180 } else { 181 r = importResourceFromTranslations(null, resource, translations, usedUnits, resource.fhirType()); 182 } 183 for (TranslationUnit t : translations) { 184 if (!usedUnits.contains(t)) { 185 messages.add(new ValidationMessage(Source.Publisher, IssueType.INFORMATIONAL, t.getId(), "Unused '"+t.getLanguage()+"' translation '"+t.getSrcText()+"' -> '"+t.getTgtText()+"'", IssueSeverity.INFORMATION)); 186 } 187 } 188 return r; 189 } 190 191 192 /* 193 * */ 194 private int importFromTranslationsForSD(Object object, Element resource, List<TranslationUnit> translations, Set<TranslationUnit> usedUnits) { 195 int r = 0; 196 r = r + checkForTranslations(translations, usedUnits, resource, "name", "name"); 197 r = r + checkForTranslations(translations, usedUnits, resource, "title", "title"); 198 r = r + checkForTranslations(translations, usedUnits, resource, "publisher", "publisher"); 199 for (Element cd : resource.getChildrenByName("contact")) { 200 r = r + checkForTranslations(translations, usedUnits, cd, "contact.name", "name"); 201 } 202 r = r + checkForTranslations(translations, usedUnits, resource, "purpose", "purpose"); 203 r = r + checkForTranslations(translations, usedUnits, resource, "copyright", "copyright"); 204 Element diff = resource.getNamedChild("differential"); 205 if (diff != null) { 206 for (Element ed : diff.getChildrenByName("element")) { 207 String id = ed.getNamedChildValue("id"); 208 r = r + checkForTranslations(translations, usedUnits, ed, id+"/label", "label"); 209 r = r + checkForTranslations(translations, usedUnits, ed, id+"/short", "short"); 210 r = r + checkForTranslations(translations, usedUnits, ed, id+"/definition", "definition"); 211 r = r + checkForTranslations(translations, usedUnits, ed, id+"/comment", "comment"); 212 r = r + checkForTranslations(translations, usedUnits, ed, id+"/requirements", "requirements"); 213 r = r + checkForTranslations(translations, usedUnits, ed, id+"/meaningWhenMissing", "meaningWhenMissing"); 214 r = r + checkForTranslations(translations, usedUnits, ed, id+"/orderMeaning", "orderMeaning"); 215 // for (ElementDefinitionConstraintComponent con : ed.getConstraint()) { 216 // addToList(list, lang, con, ed.getId()+"/constraint", "human", con.getHumanElement()); 217 // } 218 // if (ed.hasBinding()) { 219 // addToList(list, lang, ed.getBinding(), ed.getId()+"/b/desc", "description", ed.getBinding().getDescriptionElement()); 220 // for (ElementDefinitionBindingAdditionalComponent ab : ed.getBinding().getAdditional()) { 221 // addToList(list, lang, ab, ed.getId()+"/ab/doco", "documentation", ab.getDocumentationElement()); 222 // addToList(list, lang, ab, ed.getId()+"/ab/short", "shortDoco", ab.getShortDocoElement()); 223 // } 224 // } 225 } 226 } 227 return r; 228 } 229 230 private int checkForTranslations(List<TranslationUnit> translations, Set<TranslationUnit> usedUnits, Element context, String tname, String pname) { 231 int r = 0; 232 Element child = context.getNamedChild(pname); 233 if (child != null) { 234 String v = child.primitiveValue(); 235 if (v != null) { 236 for (TranslationUnit tu : translations) { 237 if (tname.equals(tu.getId()) && v.equals(tu.getSrcText())) { 238 usedUnits.add(tu); 239 child.setTranslation(tu.getLanguage(), tu.getTgtText()); 240 r++; 241 } 242 } 243 } 244 } 245 return r; 246 } 247 248 private int importResourceFromTranslations(Base parent, Base element, List<TranslationUnit> translations, Set<TranslationUnit> usedUnits, String path) { 249 int t = 0; 250 if (element.isPrimitive() && isTranslatable(element, path) && element instanceof org.hl7.fhir.r5.model.Element) { 251 org.hl7.fhir.r5.model.Element e = (org.hl7.fhir.r5.model.Element) element; 252 String base = element.primitiveValue(); 253 if (base != null) { 254 String epath = pathForElement(path, element.fhirType()); 255 Set<TranslationUnit> tlist = findTranslations(epath, base, translations); 256 for (TranslationUnit translation : tlist) { 257 t++; 258 if (!handleAsSpecial(parent, element, translation)) { 259 ToolingExtensions.setLanguageTranslation(e, translation.getLanguage(), translation.getTgtText()); 260 usedUnits.add(translation); 261 } 262 } 263 } 264 } 265 for (Property c : element.children()) { 266 for (Base v : c.getValues()) { 267 if (!c.getName().equals("designation")) { 268 t = t + importResourceFromTranslations(element, v, translations, usedUnits, path+"."+c.getName()); 269 } 270 } 271 } 272 return t; 273 } 274 275 private boolean handleAsSpecial(Base parent, Base element, TranslationUnit translation) { 276 return false; 277 } 278 279 private boolean isTranslatable(Base element, String path) { 280 return Utilities.existsInList(element.fhirType(), "string", "markdown"); 281 } 282 283 private int importFromTranslations(Element parent, Element element, List<TranslationUnit> translations, Set<TranslationUnit> usedUnits) { 284 int t = 0; 285 if (element.isPrimitive() && isTranslatable(element)) { 286 String base = element.primitiveValue(); 287 if (base != null) { 288 String path = pathForElement(element); 289 Set<TranslationUnit> tlist = findTranslations(path, base, translations); 290 for (TranslationUnit translation : tlist) { 291 t++; 292 if (!handleAsSpecial(parent, element, translation)) { 293 element.setTranslation(translation.getLanguage(), translation.getTgtText()); 294 usedUnits.add(translation); 295 } 296 } 297 } 298 } 299 for (Element c : element.getChildren()) { 300 if (!c.getName().equals("designation")) { 301 t = t + importFromTranslations(element, c, translations, usedUnits); 302 } 303 } 304 return t; 305 } 306 307 private boolean handleAsSpecial(Element parent, Element element, TranslationUnit translation) { 308 if (parent == null) { 309 return false; 310 } 311 if (Utilities.existsInList(pathForElement(parent), "CodeSystem.concept", "CodeSystem.concept.concept") && "CodeSystem.concept.display".equals(pathForElement(element))) { 312 return setDesignationTranslation(parent, translation.getLanguage(), translation.getTgtText()); 313 } 314 if (Utilities.existsInList(pathForElement(parent), "ValueSet.compose.include.concept") && "ValueSet.compose.include.concept.display".equals(pathForElement(element))) { 315 return setDesignationTranslation(parent, translation.getLanguage(), translation.getTgtText()); 316 } 317 if (Utilities.existsInList(pathForElement(parent), "ValueSet.expansion.contains", "ValueSet.expansion.contains.contains") && "ValueSet.expansion.contains.display".equals(pathForElement(element))) { 318 return setDesignationTranslation(parent, translation.getLanguage(), translation.getTgtText()); 319 } 320 return false; 321 } 322 323 private boolean setDesignationTranslation(Element parent, String targetLang, String translation) { 324 for (Element e : parent.getChildren("designation")) { 325 String lang = e.getNamedChildValue("language"); 326 if (langsMatch(targetLang, lang)) { 327 Element value = e.getNamedChild("value"); 328 if (value != null) { 329 value.setValue(translation); 330 } else { 331 e.addElement("value").setValue(translation); 332 } 333 return true; 334 } 335 } 336 Element d = parent.addElement("designation"); 337 d.addElement("language").setValue(targetLang); 338 d.addElement("value").setValue(translation); 339 return true; 340 } 341 342 private Set<TranslationUnit> findTranslations(String path, String src, List<TranslationUnit> translations) { 343 Set<TranslationUnit> res = new HashSet<>(); 344 for (TranslationUnit translation : translations) { 345 if (path.equals(translation.getId()) && src.equals(translation.getSrcText())) { 346 res.add(translation); 347 } 348 } 349 return res; 350 } 351 352 public static boolean langsMatchExact(AcceptLanguageHeader langs, String srcLang) { 353 if (langs == null) { 354 return false; 355 } 356 for (LanguagePreference lang : langs.getLangs()) { 357 if (lang.getValue() > 0) { 358 if ("*".equals(lang.getLang())) { 359 return true; 360 } else { 361 return langsMatch(lang.getLang(), srcLang); 362 } 363 } 364 } 365 return false; 366 } 367 368 public static boolean langsMatch(AcceptLanguageHeader langs, String srcLang) { 369 if (langs == null) { 370 return false; 371 } 372 for (LanguagePreference lang : langs.getLangs()) { 373 if (lang.getValue() > 0) { 374 if ("*".equals(lang.getLang())) { 375 return true; 376 } else { 377 boolean ok = langsMatch(lang.getLang(), srcLang); 378 if (ok) { 379 return true; 380 } 381 } 382 } 383 } 384 return false; 385 } 386 387 public static boolean langsMatchExact(String dstLang, String srcLang) { 388 return dstLang == null ? false : dstLang.equals(srcLang); 389 } 390 391 public static boolean langsMatch(String dstLang, String srcLang) { 392 return dstLang == null || srcLang == null ? false : dstLang.startsWith(srcLang) || "*".equals(srcLang); 393 } 394 395 public void fillSupplement(CodeSystem csSrc, CodeSystem csDst, List<TranslationUnit> list) { 396 csDst.setUserData(SUPPLEMENT_SOURCE_RESOURCE, csSrc); 397 csDst.setUserData(SUPPLEMENT_SOURCE_TRANSLATIONS, list); 398 for (TranslationUnit tu : list) { 399 String code = tu.getId(); 400 String subCode = null; 401 if (code.contains("@")) { 402 subCode = code.substring(code.indexOf("@")+1); 403 code = code.substring(0, code.indexOf("@")); 404 } 405 ConceptDefinitionComponent cdSrc = CodeSystemUtilities.getCode(csSrc, tu.getId()); 406 if (cdSrc == null) { 407 addOrphanTranslation(csSrc, tu); 408 } else { 409 ConceptDefinitionComponent cdDst = CodeSystemUtilities.getCode(csDst, cdSrc.getCode()); 410 if (cdDst == null) { 411 cdDst = csDst.addConcept().setCode(cdSrc.getCode()); 412 } 413 String tt = tu.getTgtText(); 414 if (tt.startsWith("!!")) { 415 tt = tt.substring(3); 416 } 417 if (subCode == null) { 418 cdDst.setDisplay(tt); 419 } else if ("definition".equals(subCode)) { 420 cdDst.setDefinition(tt); 421 } else { 422 boolean found = false; 423 for (ConceptDefinitionDesignationComponent d : cdSrc.getDesignation()) { 424 if (d.hasUse() && subCode.equals(d.getUse().getCode())) { 425 found = true; 426 cdDst.addDesignation().setUse(d.getUse()).setLanguage(tu.getLanguage()).setValue(tt); //.setUserData(SUPPLEMENT_SOURCE, tu); 427 break; 428 } 429 } 430 if (!found) { 431 for (Extension e : cdSrc.getExtension()) { 432 if (subCode.equals(tail(e.getUrl()))) { 433 found = true; 434 cdDst.addExtension().setUrl(e.getUrl()).setValue( 435 e.getValue().fhirType().equals("markdown") ? new MarkdownType(tt) : new StringType(tt)); //.setUserData(SUPPLEMENT_SOURCE, tu); 436 break; 437 } 438 } 439 } 440 if (!found) { 441 addOrphanTranslation(csSrc, tu); 442 } 443 } 444 } 445 } 446 } 447 448 private String tail(String url) { 449 return url.contains("/") ? url.substring(url.lastIndexOf("/")+1) : url; 450 } 451 452 private void addOrphanTranslation(CodeSystem cs, TranslationUnit tu) { 453 List<TranslationUnit> list = (List<TranslationUnit>) cs.getUserData(ORPHAN_TRANSLATIONS_NAME); 454 if (list == null) { 455 list = new ArrayList<>(); 456 cs.setUserData(ORPHAN_TRANSLATIONS_NAME, list); 457 } 458 list.add(tu); 459 } 460 461 public String nameForLang(String lang) { 462 // todo: replace with structures from loading languages properly 463 switch (lang) { 464 case "en" : return "English"; 465 case "de" : return "German"; 466 case "es" : return "Spanish"; 467 case "nl" : return "Dutch"; 468 } 469 return Utilities.capitalize(lang); 470 } 471 472 public String titleForLang(String lang) { 473 // todo: replace with structures from loading languages properly 474 switch (lang) { 475 case "en" : return "English"; 476 case "de" : return "German"; 477 case "es" : return "Spanish"; 478 case "nl" : return "Dutch"; 479 } 480 return Utilities.capitalize(lang); 481 } 482 483 public boolean handlesAsResource(Resource resource) { 484 return (resource instanceof CodeSystem && resource.hasUserData(SUPPLEMENT_SOURCE_RESOURCE)) || (resource instanceof StructureDefinition); 485 } 486 487 public boolean handlesAsElement(Element element) { 488 return true; // for now... 489 } 490 491 public List<TranslationUnit> generateTranslations(Resource res, String lang) { 492 List<TranslationUnit> list = new ArrayList<>(); 493 if (res instanceof StructureDefinition) { 494 StructureDefinition sd = (StructureDefinition) res; 495 generateTranslations(list, sd, lang); 496 if (res.hasUserData(ORPHAN_TRANSLATIONS_NAME)) { 497 List<TranslationUnit> orphans = (List<TranslationUnit>) res.getUserData(ORPHAN_TRANSLATIONS_NAME); 498 for (TranslationUnit t : orphans) { 499 list.add(new TranslationUnit(lang, "!!"+t.getId(), t.getContext1(), t.getSrcText(), t.getTgtText())); 500 } 501 } 502 } else { 503 CodeSystem cs = (CodeSystem) res.getUserData(SUPPLEMENT_SOURCE_RESOURCE); 504 List<TranslationUnit> inputs = res.hasUserData(SUPPLEMENT_SOURCE_TRANSLATIONS) ? (List<TranslationUnit>) res.getUserData(SUPPLEMENT_SOURCE_TRANSLATIONS) : new ArrayList<>(); 505 for (ConceptDefinitionComponent cd : cs.getConcept()) { 506 generateTranslations(list, cd, lang, inputs); 507 } 508 if (cs.hasUserData(ORPHAN_TRANSLATIONS_NAME)) { 509 List<TranslationUnit> orphans = (List<TranslationUnit>) cs.getUserData(ORPHAN_TRANSLATIONS_NAME); 510 for (TranslationUnit t : orphans) { 511 list.add(new TranslationUnit(lang, "!!"+t.getId(), t.getContext1(), t.getSrcText(), t.getTgtText())); 512 } 513 } 514 } 515 return list; 516 } 517 518 private void generateTranslations(List<TranslationUnit> list, StructureDefinition sd, String lang) { 519 addToList(list, lang, sd, "name", "name", sd.getNameElement()); 520 addToList(list, lang, sd, "title", "title", sd.getTitleElement()); 521 addToList(list, lang, sd, "publisher", "publisher", sd.getPublisherElement()); 522 for (ContactDetail cd : sd.getContact()) { 523 addToList(list, lang, cd, "contact.name", "name", cd.getNameElement()); 524 } 525 addToList(list, lang, sd, "purpose", "purpose", sd.getPurposeElement()); 526 addToList(list, lang, sd, "copyright", "copyright", sd.getCopyrightElement()); 527 for (ElementDefinition ed : sd.getDifferential().getElement()) { 528 addToList(list, lang, ed, ed.getId()+"/label", "label", ed.getLabelElement()); 529 addToList(list, lang, ed, ed.getId()+"/short", "short", ed.getShortElement()); 530 addToList(list, lang, ed, ed.getId()+"/definition", "definition", ed.getDefinitionElement()); 531 addToList(list, lang, ed, ed.getId()+"/comment", "comment", ed.getCommentElement()); 532 addToList(list, lang, ed, ed.getId()+"/requirements", "requirements", ed.getRequirementsElement()); 533 addToList(list, lang, ed, ed.getId()+"/meaningWhenMissing", "meaningWhenMissing", ed.getMeaningWhenMissingElement()); 534 addToList(list, lang, ed, ed.getId()+"/orderMeaning", "orderMeaning", ed.getOrderMeaningElement()); 535 for (ElementDefinitionConstraintComponent con : ed.getConstraint()) { 536 addToList(list, lang, con, ed.getId()+"/constraint", "human", con.getHumanElement()); 537 } 538 if (ed.hasBinding()) { 539 addToList(list, lang, ed.getBinding(), ed.getId()+"/b/desc", "description", ed.getBinding().getDescriptionElement()); 540 for (ElementDefinitionBindingAdditionalComponent ab : ed.getBinding().getAdditional()) { 541 addToList(list, lang, ab, ed.getId()+"/ab/doco", "documentation", ab.getDocumentationElement()); 542 addToList(list, lang, ab, ed.getId()+"/ab/short", "shortDoco", ab.getShortDocoElement()); 543 } 544 } 545 } 546 } 547 548 private void addToList(List<TranslationUnit> list, String lang, Base ctxt, String name, String propName, DataType value) { 549 if (value != null && value.hasPrimitiveValue()) { 550 list.add(new TranslationUnit(lang, name, ctxt.getNamedProperty(propName).getDefinition(), value.primitiveValue(), value.getTranslation(lang))); 551 } 552 553 } 554 555 private void generateTranslations(List<TranslationUnit> list, ConceptDefinitionComponent cd, String lang, List<TranslationUnit> inputs) { 556 // we generate translation units for the display, the definition, and any designations and extensions that we find 557 // the id of the designation is the use.code (there will be a use) and for the extension, the tail of the extension URL 558 // todo: do we need to worry about name clashes? why would we, and more importantly, how would we solve that? 559 560 addTranslationUnit(list, cd.getCode(), cd.getDisplay(), lang, inputs); 561 if (cd.hasDefinition()) { 562 addTranslationUnit(list, cd.getCode()+"@definition", cd.getDefinition(), lang, inputs); 563 } 564 for (ConceptDefinitionDesignationComponent d : cd.getDesignation()) { 565 addTranslationUnit(list, cd.getCode()+"@"+d.getUse().getCode(), d.getValue(), lang, inputs); 566 } 567 for (Extension e : cd.getExtension()) { 568 addTranslationUnit(list, cd.getCode()+"@"+tail(e.getUrl()), e.getValue().primitiveValue(), lang, inputs); 569 } 570 } 571 572 private void addTranslationUnit(List<TranslationUnit> list, String id, String srcText, String lang, List<TranslationUnit> inputs) { 573 TranslationUnit existing = null; 574 for (TranslationUnit t : inputs) { 575 if (id.equals(t.getId())) { 576 existing = t; 577 break; 578 } 579 } 580 // not sure what to do with context? 581 if (existing == null) { 582 list.add(new TranslationUnit(lang, id, null, srcText, null)); 583 } else if (srcText.equals(existing.getSrcText())) { 584 list.add(new TranslationUnit(lang, id, null, srcText, existing.getTgtText())); 585 } else { 586 list.add(new TranslationUnit(lang, id, null, srcText, "!!"+existing.getTgtText()).setOriginal(existing.getSrcText())); 587 } 588 } 589 590 private String getDefinition(ConceptDefinitionComponent cd) { 591 ConceptPropertyComponent v = CodeSystemUtilities.getProperty(cd, "translation-context"); 592 if (v != null && v.hasValue()) { 593 return v.getValue().primitiveValue(); 594 } else { 595 return cd.getDefinition(); 596 } 597 } 598 599 public List<TranslationUnit> generateTranslations(Element e, String lang) { 600 List<TranslationUnit> list = new ArrayList<>(); 601 generateTranslations(e, lang, list); 602 return list; 603 } 604 605 private void generateTranslations(Element e, String lang, List<TranslationUnit> list) { 606 if (e.getProperty().isTranslatable()) { 607 String id = pathForElement(e); // .getProperty().getDefinition().getPath(); 608 String context = e.getProperty().getDefinition().getDefinition(); 609 String src = e.primitiveValue(); 610 String tgt = getTranslation(e, lang); 611 list.add(new TranslationUnit(lang, id, context, src, tgt)); 612 } 613 if (e.hasChildren()) { 614 for (Element c : e.getChildren()) { 615 generateTranslations(c, lang, list); 616 } 617 } 618 619 } 620 621 private String getTranslation(Element e, String lang) { 622 if (!e.hasChildren()) { 623 return null; 624 } 625 for (Element ext : e.getChildren()) { 626 if ("Extension".equals(ext.fhirType()) && "http://hl7.org/fhir/StructureDefinition/translation".equals(ext.getNamedChildValue("url"))) { 627 String l = null; 628 String v = null; 629 for (Element subExt : ext.getChildren()) { 630 if ("Extension".equals(subExt.fhirType()) && "lang".equals(subExt.getNamedChildValue("url"))) { 631 l = subExt.getNamedChildValue("value"); 632 } 633 if ("Extension".equals(subExt.fhirType()) && "content".equals(subExt.getNamedChildValue("url"))) { 634 v = subExt.getNamedChildValue("value"); 635 } 636 } 637 if (lang.equals(l)) { 638 return v; 639 } 640 } 641 } 642 return null; 643 } 644 645 public boolean switchLanguage(Element e, String lang) { 646 if (e.getProperty().isTranslatable()) { 647 String cnt = getTranslation(e, lang); 648 e.removeExtension(ToolingExtensions.EXT_TRANSLATION); 649 if (cnt != null) { 650 e.setValue(cnt); 651 } 652 } 653 if (e.hasChildren()) { 654 for (Element c : e.getChildren()) { 655 if (!switchLanguage(c, lang)) { 656 return false; 657 } 658 } 659 } 660 return true; 661 } 662}