1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28 package ca.uhn.hl7v2.sourcegen;
29
30 import java.io.BufferedWriter;
31 import java.io.File;
32 import java.io.FileOutputStream;
33 import java.io.IOException;
34 import java.io.OutputStreamWriter;
35 import java.sql.Connection;
36 import java.sql.ResultSet;
37 import java.sql.SQLException;
38 import java.sql.Statement;
39 import java.util.ArrayList;
40 import java.util.Arrays;
41 import java.util.HashMap;
42 import java.util.List;
43 import java.util.Map;
44 import java.util.TreeMap;
45
46 import org.apache.velocity.Template;
47 import org.apache.velocity.VelocityContext;
48 import org.apache.velocity.context.Context;
49 import org.slf4j.Logger;
50 import org.slf4j.LoggerFactory;
51
52 import ca.uhn.hl7v2.HL7Exception;
53 import ca.uhn.hl7v2.database.NormativeDatabase;
54 import ca.uhn.hl7v2.parser.DefaultModelClassFactory;
55 import ca.uhn.hl7v2.sourcegen.util.VelocityFactory;
56
57
58
59
60
61
62
63
64 public class MessageGenerator {
65
66 private static final Logger log = LoggerFactory.getLogger(MessageGenerator.class);
67
68
69
70
71
72
73 public static final String MODEL_CLASS_FACTORY_KEY = "ca.uhn.hl7v2.sourcegen.modelclassfactory";
74
75
76 public MessageGenerator() {
77 }
78
79 public static File determineTargetDir(String baseDirectory, String version) throws IOException, HL7Exception {
80 return SourceGenerator.makeDirectory(baseDirectory + DefaultModelClassFactory.getVersionPackagePath(version) + "message");
81 }
82
83
84
85
86
87 private static String getMessageListQuery(String version) {
88
89
90
91
92
93 return "SELECT distinct message_type + '_' + event_code AS msg_struct, '?'"
94
95
96
97 + " FROM HL7Versions RIGHT JOIN HL7EventMessageTypeSegments ON HL7EventMessageTypeSegments.version_id = HL7Versions.version_id "
98 + "WHERE HL7Versions.hl7_version ='"
99 + version
100 + "' and Not (message_type='ACK') "
101 + "UNION "
102 + "select distinct HL7MsgStructIDs.message_structure, section from HL7Versions RIGHT JOIN (HL7MsgStructIDSegments "
103 + " inner join HL7MsgStructIDs on HL7MsgStructIDSegments.message_structure = HL7MsgStructIDs.message_structure "
104 + " and HL7MsgStructIDSegments.version_id = HL7MsgStructIDs.version_id) " + " ON HL7MsgStructIDSegments.version_id = HL7Versions.version_id " + " where HL7Versions.hl7_version = '" + version + "' and HL7MsgStructIDs.message_structure not like 'ACK_%'";
105
106
107
108 }
109
110 public static MessageListAndChapterList getMessages(String theVersion) throws SQLException {
111
112 NormativeDatabase normativeDatabase = NormativeDatabase.getInstance();
113 Connection conn = normativeDatabase.getConnection();
114 Statement stmt = conn.createStatement();
115 String sql = getMessageListQuery(theVersion);
116 ResultSet rs = stmt.executeQuery(sql);
117 ArrayList<String> messages = new ArrayList<>();
118 ArrayList<String> chapters = new ArrayList<>();
119 while (rs.next()) {
120 String name = rs.getString(1);
121 if ("0".equals(name)) {
122 continue;
123 }
124 messages.add(name);
125 chapters.add(rs.getString(2));
126 }
127 stmt.close();
128 normativeDatabase.returnConnection(conn);
129
130 MessageListAndChapterList retVal = new MessageListAndChapterList();
131 retVal.chapters = chapters;
132 retVal.messages = messages;
133
134 return retVal;
135 }
136
137
138
139
140
141
142
143 private static String getSegmentListQuery(String message, String version) {
144 String sql;
145
146 sql = "SELECT HL7Segments.seg_code, repetitional, optional, HL7Segments.description, seq_no, groupname " + "FROM HL7Versions RIGHT JOIN (HL7Segments INNER JOIN HL7EventMessageTypeSegments ON (HL7Segments.version_id = HL7EventMessageTypeSegments.version_id) "
147 + "AND (HL7Segments.seg_code = HL7EventMessageTypeSegments.seg_code)) " + "ON HL7Segments.version_id = HL7Versions.version_id " + "WHERE (((HL7Versions.hl7_version)= '" + version + "') "
148
149 + "AND ((message_type + '_' + event_code)='" + message + "')) order by seq_no UNION "
150
151 + "select HL7Segments.seg_code, repetitional, optional, HL7Segments.description, seq_no, groupname " + "from HL7Versions RIGHT JOIN (HL7MsgStructIDSegments inner join HL7Segments on HL7MsgStructIDSegments.seg_code = HL7Segments.seg_code "
152 + "and HL7MsgStructIDSegments.version_id = HL7Segments.version_id) " + "ON HL7Segments.version_id = HL7Versions.version_id " + "where HL7Versions.hl7_version = '" + version + "' and message_structure = '" + message + "' order by seq_no";
153 return sql;
154 }
155
156
157
158
159
160
161
162
163
164
165
166 private static SegmentDef[] getSegments(String message, String version) throws SQLException {
167
168
169
170
171
172
173
174
175 String sql = getSegmentListQuery(message, version);
176
177 SegmentDef[] segments = new SegmentDef[200];
178
179 NormativeDatabase normativeDatabase = NormativeDatabase.getInstance();
180 Connection conn = normativeDatabase.getConnection();
181 Statement stmt = conn.createStatement();
182 ResultSet rs = stmt.executeQuery(sql);
183 int c = -1;
184 boolean inChoice = false;
185 while (rs.next()) {
186 String name = SegmentGenerator.altSegName(rs.getString(1));
187 boolean repeating = rs.getBoolean(2);
188 boolean optional = rs.getBoolean(3);
189 String desc = rs.getString(4);
190
191
192
193 String groupName;
194 if (version.equalsIgnoreCase("2.3.1") && !"true".equals(System.getProperty("force.group"))) {
195 groupName = null;
196 } else {
197 groupName = rs.getString(6);
198 }
199
200 if (groupName != null) {
201
202 groupName = groupName.replaceAll(" ", "_");
203 groupName = groupName.replaceAll("/", "_");
204 }
205
206 if (name.equals("<")) {
207 inChoice = true;
208 } else if (name.equals(">")) {
209 inChoice = false;
210 } else if (!name.equals("|")) {
211 c++;
212 segments[c] = new SegmentDef(name, groupName, !optional, repeating, inChoice, desc);
213 }
214 }
215 SegmentDef[] ret = new SegmentDef[c + 1];
216 System.arraycopy(segments, 0, ret, 0, c + 1);
217
218 normativeDatabase.returnConnection(conn);
219
220 return ret;
221 }
222
223
224
225
226
227
228 public static void make(String message, String baseDirectory, String chapter, String version, String theTemplatePackage, String theFileExt) throws Exception {
229
230
231
232
233
234 try {
235 SegmentDef[] segments = getSegments(message, version);
236
237
238
239
240 GroupDef group = GroupGenerator.getGroupDef(segments, null, baseDirectory, version, message, theTemplatePackage, theFileExt);
241 StructureDef[] contents = group.getStructures();
242
243
244 if (!(baseDirectory.endsWith("\\") || baseDirectory.endsWith("/"))) {
245 baseDirectory = baseDirectory + "/";
246 }
247 File targetDir = determineTargetDir(baseDirectory, version);
248 System.out.println("Writing " + message + " to " + targetDir.getPath());
249 String fileName = targetDir.getPath() + "/" + message + "." + theFileExt;
250
251 writeMessage(fileName, contents, message, chapter, version, DefaultModelClassFactory.getVersionPackageName(version), true, theTemplatePackage, null);
252
253 } catch (SQLException | IOException e) {
254 throw new HL7Exception(e);
255 }
256
257
258
259
260
261
262
263
264
265
266 }
267
268
269
270
271 public static void makeAll(String baseDirectory, String version, boolean failOnError, String theTemplatePackage, String theFileExt) throws Exception {
272 MessageListAndChapterList mac = getMessages(version);
273 ArrayList<String> messages = mac.messages;
274 ArrayList<String> chapters = mac.chapters;
275
276 if (messages.size() == 0) {
277 log.warn("No version {} messages found in database {}", version, System.getProperty("ca.on.uhn.hl7.database.url"));
278 }
279
280 for (int i = 0; i < messages.size(); i++) {
281 String message = messages.get(i);
282
283 String hapiTestGenMessage = System.getProperty("hapi.test.genmessage");
284 if (hapiTestGenMessage != null && !hapiTestGenMessage.contains(message)) {
285 continue;
286 }
287
288 try {
289 make(message, baseDirectory, chapters.get(i), version, theTemplatePackage, theFileExt);
290 } catch (HL7Exception e) {
291 if (failOnError) {
292 throw e;
293 } else {
294 log.error("Failed to generate message" + message + ": ", e);
295 }
296 }
297 }
298 }
299
300
301
302
303 public static String makeConstructor(StructureDef[] structs, String messageName, String version) {
304 boolean useFactory = System.getProperty(MODEL_CLASS_FACTORY_KEY, "FALSE").equalsIgnoreCase("TRUE");
305
306 StringBuilder source = new StringBuilder();
307
308 source.append("\t/** \r\n");
309 source.append("\t * Creates a new ");
310 source.append(messageName);
311 source.append(" Group with custom ModelClassFactory.\r\n");
312 source.append("\t */\r\n");
313 source.append("\tpublic ");
314 source.append(messageName);
315 source.append("(ModelClassFactory factory) {\r\n");
316 source.append("\t super(factory);\r\n");
317 source.append("\t init(factory);\r\n");
318 source.append("\t}\r\n\r\n");
319 source.append("\t/**\r\n");
320 source.append("\t * Creates a new ");
321 source.append(messageName);
322 source.append(" Group with DefaultModelClassFactory. \r\n");
323 source.append("\t */ \r\n");
324 source.append("\tpublic ");
325 source.append(messageName);
326 source.append("() { \r\n");
327 source.append("\t super(new DefaultModelClassFactory());\r\n");
328 source.append("\t init(new DefaultModelClassFactory());\r\n");
329 source.append("\t}\r\n\r\n");
330 source.append("\tprivate void init(ModelClassFactory factory) {\r\n");
331 source.append("\t try {\r\n");
332 int numStructs = structs.length;
333 for (StructureDef def : structs) {
334 if (useFactory) {
335 source.append("\t this.add(factory.get");
336 source.append((def instanceof GroupDef) ? "Group" : "Segment");
337 source.append("Class(\"");
338 source.append(def.getName());
339 source.append("\", \"");
340 source.append(version);
341 source.append("\"), ");
342 } else {
343 source.append("\t this.add(");
344 source.append(def.getName());
345 source.append(".class, ");
346 }
347 source.append(def.isRequired());
348 source.append(", ");
349 source.append(def.isRepeating());
350 source.append(");\r\n");
351 }
352 source.append("\t } catch(HL7Exception e) {\r\n");
353 source.append("\t HapiLogFactory.getHapiLog(this.getClass()).error(\"Unexpected error creating ");
354 source.append(messageName);
355 source.append(" - this is probably a bug in the source code generator.\", e);\r\n");
356 source.append("\t }\r\n");
357 source.append("\t}\r\n\r\n");
358 return source.toString();
359 }
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374 public static void writeMessage(String fileName, StructureDef[] contents, String message, String chapter, String version, String basePackageName, boolean haveGroups, String theTemplatePackage, Map<String, List<String>> theStructureNameToChildNames) throws Exception {
375
376 BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName, false), SourceGenerator.ENCODING));
377
378 theTemplatePackage = theTemplatePackage.replace(".", "/");
379 String template2 = theTemplatePackage + "/messages.vsm";
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395 if (theStructureNameToChildNames != null && theStructureNameToChildNames.size() > 0) {
396 theStructureNameToChildNames = new TreeMap<>(theStructureNameToChildNames);
397 } else {
398 theStructureNameToChildNames = new HashMap<>();
399 }
400
401 Template template = VelocityFactory.getClasspathTemplateInstance(template2);
402 Context ctx = new VelocityContext();
403 ctx.put("message", message);
404 ctx.put("specVersion", version);
405 ctx.put("chapter", chapter);
406 ctx.put("haveGroups", haveGroups);
407 ctx.put("basePackageName", basePackageName);
408 ctx.put("segments", Arrays.asList(contents));
409 ctx.put("structureNameToChildNames", theStructureNameToChildNames);
410 ctx.put("HASH", "#");
411 template.merge(ctx, out);
412
413 out.flush();
414 out.close();
415 }
416
417 public static class MessageListAndChapterList {
418 ArrayList<String> chapters;
419 ArrayList<String> messages;
420 }
421
422 }