View Javadoc

1   /**
2    * The contents of this file are subject to the Mozilla Public License Version 1.1
3    * (the "License"); you may not use this file except in compliance with the License.
4    * You may obtain a copy of the License at http://www.mozilla.org/MPL/
5    * Software distributed under the License is distributed on an "AS IS" basis,
6    * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the
7    * specific language governing rights and limitations under the License.
8    *
9    * The Original Code is "SegmentGenerator.java".  Description:
10   * "This class is responsible for generating source code for HL7 segment objects"
11   *
12   * The Initial Developer of the Original Code is University Health Network. Copyright (C)
13   * 2001.  All Rights Reserved.
14   *
15   * Contributor(s):  Eric Poiseau. 
16   *
17   * Alternatively, the contents of this file may be used under the terms of the
18   * GNU General Public License (the  �GPL�), in which case the provisions of the GPL are
19   * applicable instead of those above.  If you wish to allow use of your version of this
20   * file only under the terms of the GPL and not to allow others to use your version
21   * of this file under the MPL, indicate your decision by deleting  the provisions above
22   * and replace  them with the notice and other provisions required by the GPL License.
23   * If you do not delete the provisions above, a recipient may use your version of
24   * this file under either the MPL or the GPL.
25   *
26   */
27  package ca.uhn.hl7v2.sourcegen;
28  
29  import java.io.BufferedWriter;
30  import java.io.File;
31  import java.io.FileOutputStream;
32  import java.io.IOException;
33  import java.io.OutputStreamWriter;
34  import java.sql.Connection;
35  import java.sql.ResultSet;
36  import java.sql.SQLException;
37  import java.sql.Statement;
38  import java.util.ArrayList;
39  import java.util.Collections;
40  import java.util.List;
41  
42  import org.apache.velocity.Template;
43  import org.apache.velocity.VelocityContext;
44  import org.slf4j.Logger;
45  import org.slf4j.LoggerFactory;
46  
47  import ca.uhn.hl7v2.HL7Exception;
48  import ca.uhn.hl7v2.database.NormativeDatabase;
49  import ca.uhn.hl7v2.parser.DefaultModelClassFactory;
50  import ca.uhn.hl7v2.sourcegen.util.VelocityFactory;
51  
52  /**
53   * This class is responsible for generating source code for HL7 segment objects.
54   * Each automatically generated segment inherits from AbstractSegment.
55   * 
56   * @author Bryan Tripp (bryan_tripp@sourceforge.net)
57   * @author Eric Poiseau
58   */
59  public class SegmentGenerator extends java.lang.Object {
60  
61  	private static final Logger log = LoggerFactory.getLogger(SegmentGenerator.class);
62  
63  	/**
64  	 * <p>Creates skeletal source code (without correct data structure but no business
65  	 * logic) for all segments found in the normative database.  </p>
66  	 */
67  	public static void makeAll(String baseDirectory, String version, String theTemplatePackage, String theFileExt) throws IOException, SQLException, HL7Exception {
68  		//make base directory
69  		if (!(baseDirectory.endsWith("\\") || baseDirectory.endsWith("/"))) {
70  			baseDirectory = baseDirectory + "/";
71  		}
72  		File targetDir = SourceGenerator.makeDirectory(baseDirectory + DefaultModelClassFactory.getVersionPackagePath(version) + "segment");
73  
74  		ArrayList<String> segments = getSegmentNames(version);
75  
76  		if (segments.size() == 0) {
77  			log.warn("No version {} segments found in database {}", 
78  					version, System.getProperty("ca.on.uhn.hl7.database.url"));
79  		}
80  
81  		for (int i = 0; i < segments.size(); i++) {
82  			try {
83  				String seg = (String) segments.get(i);
84  				
85  				String hapiTestGenSegment = System.getProperty("hapi.test.gensegment");
86  				if (hapiTestGenSegment != null && !hapiTestGenSegment.contains(seg)) {
87  					continue;
88  				}
89  				
90  				makeSegment(seg, version, theTemplatePackage, targetDir, theFileExt);
91  			} catch (Exception e) {
92  				System.err.println("Error creating source code for all segments: " + e.getMessage());
93  				e.printStackTrace();
94  			}
95  		}
96  	}
97  
98      public static ArrayList<String> getSegmentNames(String version) throws SQLException {
99          //get list of segments
100 		NormativeDatabase normativeDatabase = NormativeDatabase.getInstance();
101 		Connection conn = normativeDatabase.getConnection();
102 		Statement stmt = conn.createStatement();
103 		String sql = "SELECT seg_code, section from HL7Segments, HL7Versions where HL7Segments.version_id = HL7Versions.version_id AND hl7_version = '" + version + "'";
104 		//System.out.println(sql);
105 		ResultSet rs = stmt.executeQuery(sql);
106 
107 		ArrayList<String> segments = new ArrayList<String>();
108 		while (rs.next()) {
109 			String segName = rs.getString(1);
110 
111             // The DB has an invalid segment with this name
112             if ("ED".equals(segName)) {
113                 continue;
114             }
115 
116 			if (Character.isLetter(segName.charAt(0))) {
117 				segments.add(altSegName(segName));
118 			}
119 		}
120 		stmt.close();
121 		normativeDatabase.returnConnection(conn);
122         return segments;
123     }
124 
125 	/**
126 	 * <p>Returns an alternate segment name to replace the given segment name.  Substitutions
127 	 * made include:  </p>
128 	 * <ul><li>Replacing Z.. with Z</li>
129 	 *<li>Replacing ??? with ???</li></ul>
130 	 */
131 	public static String altSegName(String segmentName) {
132 		String ret = segmentName;
133 		if (ret.equals("Z..")) {
134 			ret = "Z";
135 		}
136 		if (ret.equals("CON")) {
137 			ret = "CON_";
138 		}
139 		return ret;
140 	}
141 
142 	/**
143 	 * Returns the Java source code for a class that represents the specified segment.
144 	 */
145 	public static void makeSegment(String name, String version, String theTemplatePackage, File theTargetDir, String theFileExt) throws Exception {
146 
147 		ArrayList<SegmentElement> elements = new ArrayList<SegmentElement>();
148 		String segDesc = null;
149 		SegmentElement se = null;
150 
151         NormativeDatabase normativeDatabase = NormativeDatabase.getInstance();
152 		try {
153 			Connection conn = normativeDatabase.getConnection();
154 
155 			StringBuffer sql = new StringBuffer();
156 			sql.append("SELECT HL7SegmentDataElements.seg_code, HL7SegmentDataElements.seq_no, ");
157 			sql.append("HL7SegmentDataElements.repetitional, HL7SegmentDataElements.repetitions, ");
158 			sql.append("HL7DataElements.description, HL7DataElements.length, HL7DataElements.table_id, ");
159 			sql.append("HL7SegmentDataElements.req_opt, HL7Segments.description, HL7DataElements.data_structure ");
160 			sql.append("FROM HL7Versions RIGHT JOIN (HL7Segments INNER JOIN (HL7DataElements INNER JOIN HL7SegmentDataElements ");
161 			sql.append("ON (HL7DataElements.version_id = HL7SegmentDataElements.version_id) ");
162 			sql.append("AND (HL7DataElements.data_item = HL7SegmentDataElements.data_item)) ");
163 			sql.append("ON (HL7Segments.version_id = HL7SegmentDataElements.version_id) ");
164 			sql.append("AND (HL7Segments.seg_code = HL7SegmentDataElements.seg_code)) ");
165 			sql.append("ON (HL7Versions.version_id = HL7Segments.version_id) ");
166 			sql.append("WHERE HL7SegmentDataElements.seg_code = '");
167 			sql.append(name);
168 			sql.append("' and HL7Versions.hl7_version = '");
169 			sql.append(version);
170 			sql.append("' ORDER BY HL7SegmentDataElements.seg_code, HL7SegmentDataElements.seq_no;");
171 			//System.out.println(sql.toString());  //for debugging
172 			Statement stmt = conn.createStatement();
173 			ResultSet rs = stmt.executeQuery(sql.toString());
174 
175 			List<String> usedFieldDescs = new ArrayList<String>();
176 			int index = 0;
177 			while (rs.next()) {
178 				if (segDesc == null) {
179 					segDesc = rs.getString(9);
180 				}
181 				se = new SegmentElement(name, version, index++);
182 				se.field = rs.getInt(2);
183 				se.rep = rs.getString(3);
184 				se.repetitions = rs.getInt(4);
185 				if (se.repetitions == 0) {
186 					if (se.rep == null || !se.rep.equalsIgnoreCase("Y")) {
187 						se.repetitions = 1;
188 					}
189 				}
190 				se.desc = rs.getString(5);
191 
192 				// If two fields have the same name, add "Rep 1" or "Rep 2" etc to the name
193 				String originalSeDesc = se.desc;
194 				if (usedFieldDescs.contains(se.desc)) {
195 					se.desc = se.desc + " Number " + (Collections.frequency(usedFieldDescs, originalSeDesc) + 1);
196 				}
197 				usedFieldDescs.add(originalSeDesc);
198 
199 				se.length = rs.getInt(6);
200 				se.table = rs.getInt(7);
201 				se.opt = rs.getString(8);
202 				se.type = rs.getString(10);
203 				//shorten CE_x to CE
204 				if (se.type.startsWith("CE")) {
205 					se.type = "CE";
206 				}
207 
208 				// Fix problems
209 				if (se.type.equals("-") || se.type.equals("NUL")) {
210 					se.type = "NULLDT";
211 				}
212 				
213 				/*
214 				 * ***
215 				 * index is 1-indexed here!!
216 				 * ***
217 				 */
218 				
219 				// 3454369
220 				if (version.equals("2.3") && name.equals("MRG") && index == 7) {
221 					se.type = "XPN";
222 				}
223 
224 				// https://sourceforge.net/p/hl7api/bugs/95/
225 				if (version.equals("2.3") && name.equals("ORC") && index == 14) {
226 					se.type = "XTN";
227 				}
228 
229 				// 2864817
230 				if (version.equals("2.3") && name.equals("PID") && index == 5) {
231 					se.rep = "Y";
232 					se.repetitions = -1;
233 				}
234 
235 				elements.add(se);
236 				/*System.out.println("Segment: " + name + " Field: " + se.field + " Rep: " + se.rep +
237 				" Repetitions: " + se.repetitions + " Desc: " + se.desc + " Length: " + se.length +
238 				" Table: " + se.table + " Segment Desc: " + segDesc);*/
239 			}
240 			stmt.close();
241 			normativeDatabase.returnConnection(conn);
242 		} catch (SQLException sqle) {
243 			sqle.printStackTrace();
244 			return;
245 		}
246 		
247 		String fileName = theTargetDir.toString() + "/" + name + "." + theFileExt;
248 		
249 		String basePackageName = DefaultModelClassFactory.getVersionPackageName(version);
250 		String[] datatypePackages = { basePackageName + "datatype" };
251         writeSegment(fileName, version, name, elements, segDesc, basePackageName, datatypePackages, theTemplatePackage);
252 
253 	}
254 
255 
256 
257 	public static void writeSegment(String fileName, String version, String segmentName, ArrayList<SegmentElement> elements, String description, String basePackage, String[] datatypePackages, String theTemplatePackage) throws Exception {
258 		log.debug("Writing segment: {}", fileName);
259 		
260 		BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName, false), SourceGenerator.ENCODING));
261 		
262         theTemplatePackage = theTemplatePackage.replace(".", "/");
263         Template template = VelocityFactory.getClasspathTemplateInstance(theTemplatePackage + "/segment.vsm");
264         VelocityContext ctx = new VelocityContext();
265         ctx.put("segmentName", segmentName);
266         ctx.put("typeDescription", description);
267         ctx.put("basePackageName", basePackage);
268         ctx.put("elements", elements);
269         ctx.put("datatypePackages", datatypePackages);
270         ctx.put("hl7VersionInQuotes", '"' + version + '"');
271         
272         template.merge(ctx, out);
273 		
274 //      String string = createSegmentString(version, segmentName, elements, description, basePackage, datatypePackageString);
275 //      out.write(string);
276 		
277 		out.flush();
278 		out.close();
279 	}
280 
281 }