001package ca.uhn.fhir.jpa.term.icd10cm;
002
003/*-
004 * #%L
005 * HAPI FHIR JPA Server
006 * %%
007 * Copyright (C) 2014 - 2022 Smile CDR, Inc.
008 * %%
009 * Licensed under the Apache License, Version 2.0 (the "License");
010 * you may not use this file except in compliance with the License.
011 * You may obtain a copy of the License at
012 *
013 *      http://www.apache.org/licenses/LICENSE-2.0
014 *
015 * Unless required by applicable law or agreed to in writing, software
016 * distributed under the License is distributed on an "AS IS" BASIS,
017 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
018 * See the License for the specific language governing permissions and
019 * limitations under the License.
020 * #L%
021 */
022
023import ca.uhn.fhir.jpa.entity.TermCodeSystemVersion;
024import ca.uhn.fhir.jpa.entity.TermConcept;
025import ca.uhn.fhir.jpa.entity.TermConceptParentChildLink;
026import ca.uhn.fhir.util.XmlUtil;
027import org.w3c.dom.Document;
028import org.w3c.dom.Element;
029import org.xml.sax.SAXException;
030
031import java.io.IOException;
032import java.io.Reader;
033
034import static org.apache.commons.lang3.StringUtils.isNotBlank;
035
036public class Icd10CmLoader {
037
038        private final TermCodeSystemVersion myCodeSystemVersion;
039        private int myConceptCount;
040        private static final String SEVEN_CHR_DEF = "sevenChrDef";
041        private static final String VERSION = "version";
042        private static final String EXTENSION = "extension";
043        private static final String CHAPTER = "chapter";
044        private static final String SECTION = "section";
045        private static final String DIAG = "diag";
046        private static final String NAME = "name";
047        private static final String DESC = "desc";
048
049        /**
050         * Constructor
051         */
052        public Icd10CmLoader(TermCodeSystemVersion theCodeSystemVersion) {
053                myCodeSystemVersion = theCodeSystemVersion;
054        }
055
056
057        public void load(Reader theReader) throws IOException, SAXException {
058                myConceptCount = 0;
059
060                Document document = XmlUtil.parseDocument(theReader, false, false);
061                Element documentElement = document.getDocumentElement();
062
063                // Extract version: Should only be 1 tag
064                for (Element nextVersion : XmlUtil.getChildrenByTagName(documentElement, "version")) {
065                        String versionId = nextVersion.getTextContent();
066                        if (isNotBlank(versionId)) {
067                                myCodeSystemVersion.setCodeSystemVersionId(versionId);
068                        }
069                }
070
071                // Extract Diags (codes)
072                for (Element nextChapter : XmlUtil.getChildrenByTagName(documentElement, "chapter")) {
073                        for (Element nextSection : XmlUtil.getChildrenByTagName(nextChapter, "section")) {
074                                for (Element nextDiag : XmlUtil.getChildrenByTagName(nextSection, "diag")) {
075                                        extractCode(nextDiag, null);
076                                }
077                        }
078                }
079
080        }
081
082
083        private void extractCode(Element theDiagElement, TermConcept theParentConcept) {
084                String code = theDiagElement.getElementsByTagName(NAME).item(0).getTextContent();
085                String display = theDiagElement.getElementsByTagName(DESC).item(0).getTextContent();
086
087                TermConcept concept;
088                if (theParentConcept == null) {
089                        concept = myCodeSystemVersion.addConcept();
090                } else {
091                        concept = theParentConcept.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA);
092                }
093
094                concept.setCode(code);
095                concept.setDisplay(display);
096
097                for (Element nextChildDiag : XmlUtil.getChildrenByTagName(theDiagElement, DIAG)) {
098                        extractCode(nextChildDiag, concept);
099                        if (XmlUtil.getChildrenByTagName(theDiagElement, SEVEN_CHR_DEF).size() != 0){
100                                extractExtension(theDiagElement, nextChildDiag, concept);
101                        }
102                }
103
104                myConceptCount++;
105        }
106
107        private void extractExtension(Element theDiagElement, Element theChildDiag, TermConcept theParentConcept) {
108                for (Element nextChrNote : XmlUtil.getChildrenByTagName(theDiagElement, SEVEN_CHR_DEF)){
109                        for (Element nextExtension : XmlUtil.getChildrenByTagName(nextChrNote, EXTENSION)){
110                                String baseCode = theChildDiag.getElementsByTagName(NAME).item(0).getTextContent();
111                                String sevenChar = nextExtension.getAttributes().item(0).getNodeValue();
112                                String baseDef = theChildDiag.getElementsByTagName(DESC).item(0).getTextContent();
113                                String sevenCharDef = nextExtension.getTextContent();
114
115                                TermConcept concept = theParentConcept.addChild(TermConceptParentChildLink.RelationshipTypeEnum.ISA);
116
117                                concept.setCode(getExtendedCode(baseCode, sevenChar));
118                                concept.setDisplay(getExtendedDisplay(baseDef, sevenCharDef));
119                        }
120
121                }
122        }
123
124        private String getExtendedDisplay(String theBaseDef, String theSevenCharDef) {
125                return theBaseDef + ", " + theSevenCharDef;
126        }
127
128        /**
129         * The Seventh Character must be placed at the seventh position of the code
130         * If the base code only has five characters, "X" will be used as a placeholder
131         */
132        private String getExtendedCode(String theBaseCode, String theSevenChar) {
133                String placeholder = "X";
134                String code = theBaseCode;
135                for (int i = code.length(); i < 7; i++){
136                        code += placeholder;
137                }
138                code += theSevenChar;
139                return code;
140        }
141
142        public int getConceptCount() {
143                return myConceptCount;
144        }
145
146}