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.icd10cm;
021
022import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
023import ca.uhn.fhir.jpa.entity.TermConcept;
024import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
025import ca.uhn.fhir.util.XmlUtil;
026import org.w3c.dom.Document;
027import org.w3c.dom.Element;
028import org.xml.sax.SAXException;
029
030import java.io.IOException;
031import java.io.Reader;
032
033import static org.apache.commons.lang3.StringUtils.isNotBlank;
034
035public class Icd10CmLoader {
036
037        private final TermCodeSystemVersion myCodeSystemVersion;
038        private int myConceptCount;
039        private static final String SEVEN_CHR_DEF = "sevenChrDef";
040        private static final String VERSION = "version";
041        private static final String EXTENSION = "extension";
042        private static final String CHAPTER = "chapter";
043        private static final String SECTION = "section";
044        private static final String DIAG = "diag";
045        private static final String NAME = "name";
046        private static final String DESC = "desc";
047
048        /**
049         * Constructor
050         */
051        public Icd10CmLoader(TermCodeSystemVersion theCodeSystemVersion) {
052                myCodeSystemVersion = theCodeSystemVersion;
053        }
054
055        public void load(Reader theReader) throws IOException, SAXException {
056                myConceptCount = 0;
057
058                Document document = XmlUtil.parseDocument(theReader, false, false);
059                Element documentElement = document.getDocumentElement();
060
061                // Extract version: Should only be 1 tag
062                for (Element nextVersion : XmlUtil.getChildrenByTagName(documentElement, "version")) {
063                        String versionId = nextVersion.getTextContent();
064                        if (isNotBlank(versionId)) {
065                                myCodeSystemVersion.setCodeSystemVersionId(versionId);
066                        }
067                }
068
069                // Extract Diags (codes)
070                for (Element nextChapter : XmlUtil.getChildrenByTagName(documentElement, "chapter")) {
071                        for (Element nextSection : XmlUtil.getChildrenByTagName(nextChapter, "section")) {
072                                for (Element nextDiag : XmlUtil.getChildrenByTagName(nextSection, "diag")) {
073                                        extractCode(nextDiag, null);
074                                }
075                        }
076                }
077        }
078
079        private void extractCode(Element theDiagElement, TermConcept theParentConcept) {
080                String code = theDiagElement.getElementsByTagName(NAME).item(0).getTextContent();
081                String display = theDiagElement.getElementsByTagName(DESC).item(0).getTextContent();
082
083                TermConcept concept;
084                if (theParentConcept == null) {
085                        concept = myCodeSystemVersion.addConcept();
086                } else {
087                        concept = theParentConcept.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA);
088                }
089
090                concept.setCode(code);
091                concept.setDisplay(display);
092
093                for (Element nextChildDiag : XmlUtil.getChildrenByTagName(theDiagElement, DIAG)) {
094                        extractCode(nextChildDiag, concept);
095                        if (XmlUtil.getChildrenByTagName(theDiagElement, SEVEN_CHR_DEF).size() != 0) {
096                                extractExtension(theDiagElement, nextChildDiag, concept);
097                        }
098                }
099
100                myConceptCount++;
101        }
102
103        private void extractExtension(Element theDiagElement, Element theChildDiag, TermConcept theParentConcept) {
104                for (Element nextChrNote : XmlUtil.getChildrenByTagName(theDiagElement, SEVEN_CHR_DEF)) {
105                        for (Element nextExtension : XmlUtil.getChildrenByTagName(nextChrNote, EXTENSION)) {
106                                String baseCode =
107                                                theChildDiag.getElementsByTagName(NAME).item(0).getTextContent();
108                                String sevenChar = nextExtension.getAttributes().item(0).getNodeValue();
109                                String baseDef = theChildDiag.getElementsByTagName(DESC).item(0).getTextContent();
110                                String sevenCharDef = nextExtension.getTextContent();
111
112                                TermConcept concept = theParentConcept.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA);
113
114                                concept.setCode(getExtendedCode(baseCode, sevenChar));
115                                concept.setDisplay(getExtendedDisplay(baseDef, sevenCharDef));
116                        }
117                }
118        }
119
120        private String getExtendedDisplay(String theBaseDef, String theSevenCharDef) {
121                return theBaseDef + ", " + theSevenCharDef;
122        }
123
124        /**
125         * The Seventh Character must be placed at the seventh position of the code
126         * If the base code only has five characters, "X" will be used as a placeholder
127         */
128        private String getExtendedCode(String theBaseCode, String theSevenChar) {
129                String placeholder = "X";
130                String code = theBaseCode;
131                for (int i = code.length(); i < 7; i++) {
132                        code += placeholder;
133                }
134                code += theSevenChar;
135                return code;
136        }
137
138        public int getConceptCount() {
139                return myConceptCount;
140        }
141}