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}