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}