001 package ca.uhn.hl7v2.util; 002 003 import java.io.BufferedReader; 004 import java.io.File; 005 import java.io.FileFilter; 006 import java.io.FileReader; 007 import java.io.FilenameFilter; 008 import java.io.IOException; 009 import java.util.HashMap; 010 import java.util.StringTokenizer; 011 012 import ca.uhn.hl7v2.HL7Exception; 013 import ca.uhn.hl7v2.util.Home; 014 import ca.uhn.log.HapiLog; 015 import ca.uhn.log.HapiLogFactory; 016 017 /** 018 * <p>Implements CodeMapper using files to store code values. Files are arranged 019 * in the following directory structure. The base directory is called "codemap". 020 * This should be created under the hapi.home directory (see Home class; defaults to .). 021 * Under the base directory, there should be one directory for each interface, and 022 * each of these directories should be named after the interface. For example if you 023 * had interfaces to pharmacy and lab systems you might have the following directories:</p> 024 * <p> <hapi.home>/codemap/pharmacy<br> 025 * <hapi.home>/codemap/lab</p> 026 * <p>Each directory should contain two files per HL7 table, named after the table numbers as 027 * follows: "hl7nnnn.li" and "hl7nnnn.il", where nnnn is the 4 digit table number. The .il 028 * file contains maps from interface codes to local codes, and the .li file contains maps from 029 * local codes to interface codes (these unfortunately may not be symmetrical).</p> 030 * <p>Each line of a file contains a single code map, with the "from" value and the "to" value 031 * separated by a tab. For example, the file <hapi.home>/lab/HL70001.li (to map local codes to interface 032 * codes for the HL7 admnistrative sex table in your lab system interface) might contain the 033 * following line: </p> 034 * <p>male&tab;M</p> 035 * <p>This means that the local code "male" maps to the interface code "M".</p> 036 * <p>Lines that start with "//" are treated as comments.</p> 037 * @author Bryan Tripp 038 */ 039 public class FileCodeMapper extends CodeMapper { 040 041 private static final HapiLog log = HapiLogFactory.getHapiLog(FileCodeMapper.class); 042 043 private boolean throwIfNoMatch = false; 044 File baseDir; 045 private HashMap interfaceToLocal; 046 private HashMap localToInterface; 047 048 /** 049 * Creates a new instance of FileCodeMapper. You should probably not 050 * construct a FileCodeMapper directly. Use CodeMapper.getInstance() 051 * instead ... this will ensure that only a single instance is created, 052 * which is important for performance because code maps are loaded from 053 * disk every time this constructor is called. 054 */ 055 public FileCodeMapper() throws HL7Exception { 056 baseDir = new File(Home.getHomeDirectory().getAbsolutePath() + "/codemap"); 057 refreshCache(); 058 } 059 060 /** 061 * If values are cached in such a way that they are not guaranteed to be current, a call 062 * to this method refreshes the values. 063 */ 064 public void refreshCache() throws HL7Exception { 065 localToInterface = new HashMap(10); 066 interfaceToLocal = new HashMap(10); 067 068 log.info("Refreshing cache"); 069 070 try { 071 //get list of child directories 072 File[] interfaceDirs = this.baseDir.listFiles(new FileFilter() { 073 public boolean accept(File pathname) { 074 boolean acc = false; 075 if (pathname.isDirectory()) 076 acc = true; 077 return acc; 078 } 079 }); 080 081 //loop through directories and set up maps 082 for (int i = 0; i < interfaceDirs.length; i++) { 083 084 log.info( 085 "Checking directory " + interfaceDirs[i].getName() + " for interface code maps."); 086 087 //get list of .li (local -> interface) and .il (interface -> local) files 088 File[] mapFiles = interfaceDirs[i].listFiles(new FilenameFilter() { 089 public boolean accept(File dir, String name) { 090 boolean acc = false; 091 if (name.toUpperCase().startsWith("HL7")) { 092 if (name.substring(name.lastIndexOf('.')).equals(".li") 093 || name.substring(name.lastIndexOf('.')).equals(".il")) 094 acc = true; 095 } 096 return acc; 097 } 098 }); 099 100 //read map entries from each file and add to hash maps for li and il codes 101 HashMap li = new HashMap(50); 102 HashMap il = new HashMap(50); 103 for (int j = 0; j < mapFiles.length; j++) { 104 log.info("Reading map entries from file " + mapFiles[j]); 105 106 String fName = mapFiles[j].getName(); 107 String tableName = fName.substring(0, fName.lastIndexOf('.')); 108 String mapDirection = fName.substring(fName.lastIndexOf('.') + 1); 109 110 //read values and store in HashMap 111 BufferedReader in = new BufferedReader(new FileReader(mapFiles[j])); 112 HashMap codeMap = new HashMap(25); 113 while (in.ready()) { 114 String line = in.readLine(); 115 if (!line.startsWith("//")) { 116 StringTokenizer tok = new StringTokenizer(line, "\t", false); 117 String from = null; 118 String to = null; 119 if (tok.hasMoreTokens()) 120 from = tok.nextToken(); 121 if (tok.hasMoreTokens()) 122 to = tok.nextToken(); 123 if (from != null && to != null) 124 codeMap.put(from, to); 125 } 126 } 127 128 //add to appropriate map for this interface 129 if (mapDirection.equals("il")) { 130 il.put(tableName.toUpperCase(), codeMap); 131 log.debug("Adding " 132 + codeMap.size() 133 + " codes to interface -> local map for " 134 + tableName 135 + " in " 136 + interfaceDirs[i].getName() 137 + " interface"); 138 } 139 else { 140 li.put(tableName.toUpperCase(), codeMap); 141 log.debug("Adding " 142 + codeMap.size() 143 + " codes to local -> interface map for " 144 + tableName 145 + " in " 146 + interfaceDirs[i].getName() 147 + " interface"); 148 } 149 } 150 151 //add maps for this interface (this directory) to global list 152 interfaceToLocal.put(interfaceDirs[i].getName(), il); 153 localToInterface.put(interfaceDirs[i].getName(), li); 154 } 155 156 } 157 catch (IOException e) { 158 throw new HL7Exception( 159 "Can't read interface code maps from disk", 160 HL7Exception.APPLICATION_INTERNAL_ERROR, 161 e); 162 } 163 } 164 165 /** 166 * Returns the local code for the given interface code as it appears in 167 * the given interface. 168 */ 169 public String getLocalCode(String interfaceName, int hl7Table, String interfaceCode) throws HL7Exception { 170 String localCode = null; 171 try { 172 HashMap interfaceMap = (HashMap) interfaceToLocal.get(interfaceName); 173 localCode = getCode(interfaceMap, hl7Table, interfaceCode); 174 } 175 catch (NullPointerException npe) { 176 if (this.throwIfNoMatch) 177 throw new HL7Exception( 178 "No local mapping for the interface code " 179 + interfaceCode 180 + " for HL7 table " 181 + hl7Table 182 + " for the interface '" 183 + interfaceName 184 + "'", 185 HL7Exception.TABLE_VALUE_NOT_FOUND); 186 } 187 return localCode; 188 } 189 190 /** 191 * Common code for getLocalcode and getInterfaceCode 192 */ 193 private String getCode(HashMap interfaceMap, int hl7Table, String code) { 194 String ret = null; 195 196 //get map for the given table 197 StringBuffer tableName = new StringBuffer(); 198 tableName.append("HL7"); 199 if (hl7Table < 1000) 200 tableName.append("0"); 201 if (hl7Table < 100) 202 tableName.append("0"); 203 if (hl7Table < 10) 204 tableName.append("0"); 205 tableName.append(hl7Table); 206 HashMap tableMap = (HashMap) interfaceMap.get(tableName.toString()); 207 208 //get code 209 ret = tableMap.get(code).toString(); 210 return ret; 211 } 212 213 /** 214 * Returns the interface code for the given local code, for use in the context 215 * of the given interface. 216 */ 217 public String getInterfaceCode(String interfaceName, int hl7Table, String localCode) throws HL7Exception { 218 String interfaceCode = null; 219 try { 220 HashMap interfaceMap = (HashMap) localToInterface.get(interfaceName); 221 interfaceCode = getCode(interfaceMap, hl7Table, localCode); 222 } 223 catch (NullPointerException npe) { 224 if (this.throwIfNoMatch) 225 throw new HL7Exception( 226 "No interface mapping for the local code " 227 + localCode 228 + " for HL7 table " 229 + hl7Table 230 + " for the interface '" 231 + interfaceName 232 + "'", 233 HL7Exception.TABLE_VALUE_NOT_FOUND); 234 } 235 return interfaceCode; 236 } 237 238 /** 239 * Determines what happens if no matching code is found during a lookup. If set to true, 240 * an HL7Exception is thrown if there is no match. If false, null is returned. The default 241 * is false. 242 */ 243 public void throwExceptionIfNoMatch(boolean throwException) { 244 this.throwIfNoMatch = throwException; 245 } 246 247 /** 248 * Test harness. 249 */ 250 public static void main(String args[]) { 251 try { 252 //FileCodeMapper mapper = new FileCodeMapper(); 253 CodeMapper.getInstance().throwExceptionIfNoMatch(true); 254 System.out.println("Local code for M is " + CodeMapper.getLocal("test", 1, "M")); 255 System.out.println("Interface code for female is " + CodeMapper.getInt("test", 1, "female")); 256 257 } 258 catch (HL7Exception e) { 259 e.printStackTrace(); 260 } 261 } 262 263 }