001/*- 002 * #%L 003 * HAPI FHIR JPA Server 004 * %% 005 * Copyright (C) 2014 - 2024 Smile CDR, Inc. 006 * %% 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * You may obtain a copy of the License at 010 * 011 * http://www.apache.org/licenses/LICENSE-2.0 012 * 013 * Unless required by applicable law or agreed to in writing, software 014 * distributed under the License is distributed on an "AS IS" BASIS, 015 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 016 * See the License for the specific language governing permissions and 017 * limitations under the License. 018 * #L% 019 */ 020package ca.uhn.fhir.jpa.term.icd10; 021 022import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion; 023import ca.uhn.fhir.jpa.entity.TermConcept; 024import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink; 025import org.hl7.fhir.r4.model.CodeSystem; 026import org.w3c.dom.Document; 027import org.w3c.dom.Element; 028import org.xml.sax.SAXException; 029 030import java.io.IOException; 031import java.io.Reader; 032import java.util.HashMap; 033import java.util.Map; 034import java.util.Optional; 035 036import static ca.uhn.fhir.util.XmlUtil.getChildrenByTagName; 037import static ca.uhn.fhir.util.XmlUtil.parseDocument; 038 039public class Icd10Loader { 040 041 public static final String EXPECTED_ROOT_NODE = "ClaML"; 042 private final CodeSystem codeSystem; 043 private final TermCodeSystemVersion codeSystemVersion; 044 private int conceptCount = 0; 045 046 public Icd10Loader(CodeSystem codeSystem, TermCodeSystemVersion codeSystemVersion) { 047 this.codeSystem = codeSystem; 048 this.codeSystemVersion = codeSystemVersion; 049 } 050 051 public void load(Reader reader) throws IOException, SAXException { 052 Document document = parseDocument(reader, false, true); 053 Element documentElement = document.getDocumentElement(); 054 055 String rootNodeName = documentElement.getTagName(); 056 if (!EXPECTED_ROOT_NODE.equals(rootNodeName)) { 057 return; 058 } 059 060 for (Element title : getChildrenByTagName(documentElement, "Title")) { 061 String name = title.getAttribute("name"); 062 if (!name.isEmpty()) { 063 codeSystem.setName(name); 064 codeSystem.setTitle(name); 065 } 066 String version = title.getAttribute("version"); 067 if (!version.isEmpty()) { 068 codeSystemVersion.setCodeSystemVersionId(version); 069 } 070 codeSystem.setDescription(title.getTextContent()); 071 } 072 073 Map<String, TermConcept> conceptMap = new HashMap<>(); 074 for (Element aClass : getChildrenByTagName(documentElement, "Class")) { 075 String code = aClass.getAttribute("code"); 076 if (code.isEmpty()) { 077 continue; 078 } 079 080 boolean rootConcept = getChildrenByTagName(aClass, "SuperClass").isEmpty(); 081 TermConcept termConcept = rootConcept ? codeSystemVersion.addConcept() : new TermConcept(); 082 termConcept.setCode(code); 083 084 // Preferred label and other properties 085 for (Element rubric : getChildrenByTagName(aClass, "Rubric")) { 086 String kind = rubric.getAttribute("kind"); 087 Optional<Element> firstLabel = 088 getChildrenByTagName(rubric, "Label").stream().findFirst(); 089 if (firstLabel.isPresent()) { 090 String textContent = firstLabel.get().getTextContent(); 091 if (textContent != null && !textContent.isEmpty()) { 092 textContent = 093 textContent.replace("\n", "").replace("\r", "").replace("\t", ""); 094 if (kind.equals("preferred")) { 095 termConcept.setDisplay(textContent); 096 } else { 097 termConcept.addPropertyString(kind, textContent); 098 } 099 } 100 } 101 } 102 103 for (Element superClass : getChildrenByTagName(aClass, "SuperClass")) { 104 TermConcept parent = conceptMap.get(superClass.getAttribute("code")); 105 if (parent != null) { 106 parent.addChild(termConcept, TermConceptParentChildLink.RelationshipTypeEnum.ISA); 107 } 108 } 109 110 conceptMap.put(code, termConcept); 111 } 112 113 conceptCount = conceptMap.size(); 114 } 115 116 public int getConceptCount() { 117 return conceptCount; 118 } 119}