View Javadoc
1   package ca.uhn.hl7v2.sourcegen;
2   
3   import java.io.BufferedWriter;
4   import java.io.File;
5   import java.io.FileOutputStream;
6   import java.io.FileWriter;
7   import java.io.IOException;
8   import java.io.OutputStreamWriter;
9   import java.io.Writer;
10  import java.sql.Connection;
11  import java.sql.ResultSet;
12  import java.sql.SQLException;
13  import java.sql.Statement;
14  import java.util.ArrayList;
15  import java.util.Collections;
16  import java.util.Comparator;
17  import java.util.List;
18  import java.util.Map;
19  import java.util.Set;
20  import java.util.TreeMap;
21  import java.util.TreeSet;
22  
23  import org.apache.commons.lang.StringUtils;
24  import org.apache.velocity.Template;
25  import org.apache.velocity.VelocityContext;
26  import org.apache.velocity.context.Context;
27  
28  import ca.uhn.hl7v2.database.NormativeDatabase;
29  import ca.uhn.hl7v2.parser.DefaultModelClassFactory;
30  import ca.uhn.hl7v2.sourcegen.util.VelocityFactory;
31  
32  public class EventMapGenerator {
33  	private static final org.slf4j.Logger ourLog = org.slf4j.LoggerFactory.getLogger(EventMapGenerator.class);
34  
35  	private static ResultSet createStructureQuery(String theVersion) throws SQLException {
36  		String query = "SELECT " + "   HL7EventMessageTypes.event_code, HL7EventMessageTypes.message_typ_snd, " + "   HL7Versions.hl7_version, HL7EventMessageTypes.message_structure_snd " + "FROM " + "   HL7Versions "
37  				+ "INNER JOIN HL7EventMessageTypes ON HL7Versions.version_id = HL7EventMessageTypes.version_id " + "WHERE " + "   HL7Versions.hl7_version = '" + theVersion + "' " + "ORDER BY HL7EventMessageTypes.message_typ_snd, HL7EventMessageTypes.event_code;";
38  
39  		NormativeDatabase normativeDatabase = NormativeDatabase.getInstance();
40  		Connection conn = normativeDatabase.getConnection();
41  		Statement stmt = conn.createStatement();
42  		ResultSet rs = stmt.executeQuery(query);
43  
44  		normativeDatabase.returnConnection(conn);
45  		return rs;
46  	}
47  
48  	public static void generateEventDesc(String theTargetDirectory, String theVersion, String theTemplatePackage) throws Exception {
49  
50  		List<TriggerDesc> triggerDescs = getTriggers(theVersion);
51  		// Structures are not explicitly defined in 2.1 and 2.2
52  		if ("2.1".equals(theVersion) || "2.2".equals(theVersion)) {
53  			for (TriggerDesc next : triggerDescs) {
54  				next.setStructure(next.getType().replace("^", "_"));
55  			}
56  		}
57  		Collections.sort(triggerDescs);
58  
59  		List<String> segments = SegmentGenerator.getSegmentNames(theVersion);
60  		Collections.sort(segments);
61  
62  		List<String> dataTypes = DataTypeGenerator.getDataTypes(theVersion);
63  		Collections.sort(dataTypes);
64  
65  		if (!(theTargetDirectory.endsWith("\\") || theTargetDirectory.endsWith("/"))) {
66  			theTargetDirectory = theTargetDirectory + "/";
67  		}
68  		String fileName = theTargetDirectory + DefaultModelClassFactory.getVersionPackagePath(theVersion) + "available_structures.json";
69  		BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName, false), SourceGenerator.ENCODING));
70  
71  		theTemplatePackage = theTemplatePackage.replace(".", "/");
72  		Template template = VelocityFactory.getClasspathTemplateInstance(theTemplatePackage + "/available_structures.vsm");
73  		Context ctx = new VelocityContext();
74  		ctx.put("triggers", triggerDescs);
75  		ctx.put("segments", segments);
76  		ctx.put("datatypes", dataTypes);
77  
78  		template.merge(ctx, out);
79  
80  		// String string = createSegmentString(version, segmentName, elements,
81  		// description, basePackage, datatypePackageString);
82  		// out.write(string);
83  
84  		out.flush();
85  		out.close();
86  
87  	}
88  
89  	public static void generateEventMap(String theOutputPath, String theVersion) throws SQLException, IOException {
90  
91  		ArrayList<String> messages = MessageGenerator.getMessages(theVersion).messages;
92  
93  		theOutputPath = theOutputPath + "/ca/uhn/hl7v2/parser/eventmap/";
94  
95  		new File(theOutputPath).mkdirs();
96  
97  		File file = new File(theOutputPath + theVersion + ".properties");
98  		file.delete();
99  		file.createNewFile();
100 		Writer writer = new FileWriter(file);
101 		writer = new BufferedWriter(writer);
102 
103 		writer.append("#event -> structure map for HL7 ").append(theVersion).append("\r\n");
104 		if ("2.1".equals(theVersion) || "2.2".equals(theVersion)) {
105 			writer.append("#note: no mappings are defined for 2.1 and 2.2");
106 			writer.close();
107 			return;
108 		}
109 
110 		ResultSet rs = createStructureQuery(theVersion);
111 		Map<String, Set<String>> trigger2structure = new TreeMap<>();
112 		while (rs.next()) {
113 			String messageType = rs.getString("message_typ_snd");
114 			String triggerCode = rs.getString("event_code");
115 			final String trigger = messageType + "_" + triggerCode;
116 			String structure = rs.getString("message_structure_snd");
117 
118 			if (messages.contains(trigger)) {
119 				ourLog.info("Skipping Eventmap for trigger {} because a structure exists!", trigger);
120 				continue;
121 			}
122 
123 			if ("ACK".equals(messageType)) {
124 				continue;
125 			}
126 			if ("NUL".equals(structure)) {
127 				continue;
128 			}
129 
130 			/*
131 			 * This logic keeps the structure value which matches exactly the
132 			 * trigger value at the end of the list. This is desirable as
133 			 * sometimes multiple structures mark themselves as being associated
134 			 * with the same trigger event, so we want to favour the one with
135 			 * the matching name.
136 			 * 
137 			 * Example: for an ORM^R01, favour creating an ORM_O01 structure and
138 			 * not an OMD_O01 even though the latter also declares itself as
139 			 * being the structure for ORM^R01 in the database.
140 			 */
141 
142 			if (!trigger2structure.containsKey(trigger)) {
143 				trigger2structure.put(trigger, new TreeSet<>((theO1, theO2) -> {
144 					if (theO1.equals(theO2)) {
145 						return 0;
146 					} else if (theO1.equals(trigger)) {
147 						return 1;
148 					} else if (theO2.equals(trigger)) {
149 						return -1;
150 					} else {
151 						return theO1.compareTo(theO2);
152 					}
153 				}));
154 			}
155 
156 			trigger2structure.get(trigger).add(structure);
157 
158 		}
159 
160 		for (Map.Entry<String, Set<String>> nextEntry : trigger2structure.entrySet()) {
161 			for (String nextStructure : nextEntry.getValue()) {
162 				writer.append(nextEntry.getKey()).append("\t").append(nextStructure).append("\r\n");
163 			}
164 		}
165 
166 		rs.close();
167 		writer.close();
168 	}
169 
170 	private static List<TriggerDesc> getTriggers(String theVersion) throws SQLException {
171 		String query = //
172 		"SELECT HL7Versions.hl7_version, HL7EventMessageTypes.message_typ_snd, HL7Events.event_code, " + "       HL7EventMessageTypes.message_structure_snd, HL7Events.description," + "        HL7EventMessageTypes.message_structure_return " + "FROM   HL7Versions "
173 				+ "INNER JOIN (HL7Events INNER JOIN HL7EventMessageTypes ON (HL7Events.version_id = HL7EventMessageTypes.version_id) AND (HL7Events.event_code = HL7EventMessageTypes.event_code)) ON HL7Versions.version_id = HL7Events.version_id"
174 				+ " WHERE HL7Versions.hl7_version = '" + theVersion + "';";
175 
176 		NormativeDatabase normativeDatabase = NormativeDatabase.getInstance();
177 		Connection conn = normativeDatabase.getConnection();
178 		List<TriggerDesc> triggerDescs = new ArrayList<>();
179 		try {
180 			Statement stmt = conn.createStatement();
181 			ResultSet rs = stmt.executeQuery(query);
182 
183 			while (rs.next()) {
184 				String version = rs.getString("hl7_version");
185 				String type = rs.getString("message_typ_snd") + "^" + rs.getString("event_code");
186 				String structure = rs.getString("message_structure_snd");
187 				String description = StringUtils.defaultString(rs.getString("description"));
188 				String returnStructure = rs.getString("message_structure_return");
189 
190 				TriggerDesc td = new TriggerDesc();
191 				td.setVersion(version);
192 				td.setType(type);
193 				td.setStructure(structure);
194 				td.setDescription(description.replace("\"", "\\\""));
195 				td.setReturnStructure(returnStructure);
196 
197 				triggerDescs.add(td);
198 
199 			}
200 
201 		} finally {
202 			normativeDatabase.returnConnection(conn);
203 		}
204 		return triggerDescs;
205 	}
206 
207 }