From fd6dcf636315eac7e81168601936beead5f7c403 Mon Sep 17 00:00:00 2001 From: Tadgh Date: Wed, 25 Aug 2021 18:16:47 -0400 Subject: [PATCH] Fix bug in code. Add test. Add changelog --- .../2920-lookup-language-by-lang-only.yaml | 6 + .../CommonCodeSystemsTerminologyService.java | 135 +++++++++++------- ...mmonCodeSystemsTerminologyServiceTest.java | 7 + 3 files changed, 96 insertions(+), 52 deletions(-) create mode 100644 hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2920-lookup-language-by-lang-only.yaml diff --git a/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2920-lookup-language-by-lang-only.yaml b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2920-lookup-language-by-lang-only.yaml new file mode 100644 index 00000000000..f77e2309f48 --- /dev/null +++ b/hapi-fhir-docs/src/main/resources/ca/uhn/hapi/fhir/changelog/5_6_0/2920-lookup-language-by-lang-only.yaml @@ -0,0 +1,6 @@ +--- +type: fix +issue: 2920 +jira: SMILE-2971 +title: "Previously, validation against bcp47 (urn:ietf:bcp:47) as a language would fail validation if the region was absent. This has been fixed, and the validate +operation will now correctly validate simple languages, e.g. `nl` instead of requiring `nl-DE` or `nl-NL`" diff --git a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java index eae628d2965..f4891ed7398 100644 --- a/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java +++ b/hapi-fhir-validation/src/main/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyService.java @@ -257,69 +257,100 @@ public class CommonCodeSystemsTerminologyService implements IValidationSupport { Map languagesMap = myLanguagesLanugageMap; Map regionsMap = myLanguagesRegionMap; if (languagesMap == null || regionsMap == null) { + initializeBcp47LanguageMap(); + } - ourLog.info("Loading BCP47 Language Registry"); + int langRegionSeparatorIndex = StringUtils.indexOfAny(theCode, '-', '_'); + boolean hasRegionAndCodeSegments = langRegionSeparatorIndex > 0; + String language; + String region; - String input = ClasspathUtil.loadResource("org/hl7/fhir/common/hapi/validation/support/registry.json"); - ArrayNode map; - try { - map = (ArrayNode) new ObjectMapper().readTree(input); - } catch (JsonProcessingException e) { - throw new ConfigurationException(e); + if (hasRegionAndCodeSegments) { + language = myLanguagesLanugageMap.get(theCode.substring(0, langRegionSeparatorIndex)); + region = myLanguagesRegionMap.get(theCode.substring(langRegionSeparatorIndex + 1)); + + if (language == null || region == null) { + //In case the user provides both a language and a region, they must both be valid for the lookup to succeed. + ourLog.warn("Couldn't find a valid bcp47 language-region combination from code: {}", theCode); + return buildNotFoundLookupCodeResult(theCode); + } else { + return buildLookupResultForLanguageAndRegion(theCode, language, region); } - - languagesMap = new HashMap<>(); - regionsMap = new HashMap<>(); - - for (int i = 0; i < map.size(); i++) { - ObjectNode next = (ObjectNode) map.get(i); - String type = next.get("Type").asText(); - if ("language".equals(type)) { - String language = next.get("Subtag").asText(); - ArrayNode descriptions = (ArrayNode) next.get("Description"); - String description = null; - if (descriptions.size() > 0) { - description = descriptions.get(0).asText(); - } - languagesMap.put(language, description); - } - if ("region".equals(type)) { - String region = next.get("Subtag").asText(); - ArrayNode descriptions = (ArrayNode) next.get("Description"); - String description = null; - if (descriptions.size() > 0) { - description = descriptions.get(0).asText(); - } - regionsMap.put(region, description); - } - + } else { + //In case user has only provided a language, we build the lookup from only that. + language = myLanguagesLanugageMap.get(theCode); + if (language == null) { + ourLog.warn("Couldn't find a valid bcp47 language from code: {}", theCode); + return buildNotFoundLookupCodeResult(theCode); + } else { + return buildLookupResultForLanguage(theCode, language); } + } + } + private LookupCodeResult buildLookupResultForLanguageAndRegion(@Nonnull String theOriginalCode, @Nonnull String theLanguage, @Nonnull String theRegion) { + LookupCodeResult lookupCodeResult = buildNotFoundLookupCodeResult(theOriginalCode); + lookupCodeResult.setCodeDisplay(theLanguage + " " + theRegion); + lookupCodeResult.setFound(true); + return lookupCodeResult; + } + private LookupCodeResult buildLookupResultForLanguage(@Nonnull String theOriginalCode, @Nonnull String theLanguage) { + LookupCodeResult lookupCodeResult = buildNotFoundLookupCodeResult(theOriginalCode); + lookupCodeResult.setCodeDisplay(theLanguage); + lookupCodeResult.setFound(true); + return lookupCodeResult; + } - ourLog.info("Have {} languages and {} regions", languagesMap.size(), regionsMap.size()); + private LookupCodeResult buildNotFoundLookupCodeResult(@Nonnull String theOriginalCode) { + LookupCodeResult lookupCodeResult = new LookupCodeResult(); + lookupCodeResult.setFound(false); + lookupCodeResult.setSearchedForSystem(LANGUAGES_CODESYSTEM_URL); + lookupCodeResult.setSearchedForCode(theOriginalCode); + return lookupCodeResult; + } - myLanguagesLanugageMap = languagesMap; - myLanguagesRegionMap = regionsMap; + private void initializeBcp47LanguageMap() { + Map regionsMap; + Map languagesMap; + ourLog.info("Loading BCP47 Language Registry"); + + String input = ClasspathUtil.loadResource("org/hl7/fhir/common/hapi/validation/support/registry.json"); + ArrayNode map; + try { + map = (ArrayNode) new ObjectMapper().readTree(input); + } catch (JsonProcessingException e) { + throw new ConfigurationException(e); } - int idx = StringUtils.indexOfAny(theCode, '-', '_'); - String language = null; - String region = null; - if (idx > 0) { - language = languagesMap.get(theCode.substring(0, idx)); - region = regionsMap.get(theCode.substring(idx + 1)); + languagesMap = new HashMap<>(); + regionsMap = new HashMap<>(); + + for (int i = 0; i < map.size(); i++) { + ObjectNode next = (ObjectNode) map.get(i); + String type = next.get("Type").asText(); + if ("language".equals(type)) { + String language = next.get("Subtag").asText(); + ArrayNode descriptions = (ArrayNode) next.get("Description"); + String description = null; + if (descriptions.size() > 0) { + description = descriptions.get(0).asText(); + } + languagesMap.put(language, description); + } + if ("region".equals(type)) { + String region = next.get("Subtag").asText(); + ArrayNode descriptions = (ArrayNode) next.get("Description"); + String description = null; + if (descriptions.size() > 0) { + description = descriptions.get(0).asText(); + } + regionsMap.put(region, description); + } } - LookupCodeResult retVal = new LookupCodeResult(); - retVal.setSearchedForCode(theCode); - retVal.setSearchedForSystem(LANGUAGES_CODESYSTEM_URL); + ourLog.info("Have {} languages and {} regions", languagesMap.size(), regionsMap.size()); - if (language != null && region != null) { - String display = language + " " + region; - retVal.setFound(true); - retVal.setCodeDisplay(display); - } - - return retVal; + myLanguagesLanugageMap = languagesMap; + myLanguagesRegionMap = regionsMap; } @Nonnull diff --git a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java index 5353864fc45..7204984a456 100644 --- a/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java +++ b/hapi-fhir-validation/src/test/java/org/hl7/fhir/common/hapi/validation/support/CommonCodeSystemsTerminologyServiceTest.java @@ -105,6 +105,13 @@ public class CommonCodeSystemsTerminologyServiceTest { assertEquals("English (United States)", outcome.getDisplay()); } + @Test + public void testLanguages_CommonLanguagesVs_OnlyLanguage_NoRegion() { + IValidationSupport.LookupCodeResult nl = mySvc.lookupCode(newSupport(), "urn:ietf:bcp:47", "nl"); + assertTrue(nl.isFound()); + assertEquals("Dutch", nl.getCodeDisplay()); + } + @Test public void testLanguages_CommonLanguagesVs_BadCode() { IValidationSupport.CodeValidationResult outcome = mySvc.validateCode(newSupport(), newOptions(), "urn:ietf:bcp:47", "FOO", null, "http://hl7.org/fhir/ValueSet/languages");