/** * The contents of this file are subject to the Mozilla Public License Version 1.1 * (the "License"); you may not use this file except in compliance with the License. * You may obtain a copy of the License at http://www.mozilla.org/MPL/ * Software distributed under the License is distributed on an "AS IS" basis, * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for the * specific language governing rights and limitations under the License. * * The Original Code is "SegmentGenerator.java". Description: * "This class is responsible for generating source code for HL7 segment objects" * * The Initial Developer of the Original Code is University Health Network. Copyright (C) * 2001. All Rights Reserved. * * Contributor(s): Eric Poiseau. * * Alternatively, the contents of this file may be used under the terms of the * GNU General Public License (the �GPL�), in which case the provisions of the GPL are * applicable instead of those above. If you wish to allow use of your version of this * file only under the terms of the GPL and not to allow others to use your version * of this file under the MPL, indicate your decision by deleting the provisions above * and replace them with the notice and other provisions required by the GPL License. * If you do not delete the provisions above, a recipient may use your version of * this file under either the MPL or the GPL. * */ package ca.uhn.hl7v2.sourcegen; import java.sql.Connection; import java.sql.Statement; import java.sql.ResultSet; import java.sql.SQLException; import java.io.BufferedWriter; import java.io.FileOutputStream; import java.io.FileWriter; import java.io.IOException; import java.io.File; import java.io.OutputStreamWriter; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; import org.apache.velocity.Template; import org.apache.velocity.VelocityContext; import org.apache.velocity.context.Context; import ca.uhn.hl7v2.HL7Exception; import ca.uhn.hl7v2.database.NormativeDatabase; import ca.uhn.hl7v2.parser.DefaultModelClassFactory; import ca.uhn.hl7v2.sourcegen.util.VelocityFactory; import ca.uhn.log.HapiLog; import ca.uhn.log.HapiLogFactory; /** * This class is responsible for generating source code for HL7 segment objects. * Each automatically generated segment inherits from AbstractSegment. * * @author Bryan Tripp (bryan_tripp@sourceforge.net) * @author Eric Poiseau */ public class SegmentGenerator extends java.lang.Object { private static final HapiLog log = HapiLogFactory.getHapiLog(SegmentGenerator.class); /** *

Creates skeletal source code (without correct data structure but no business * logic) for all segments found in the normative database.

*/ public static void makeAll(String baseDirectory, String version, String theTemplatePackage, String theFileExt) throws IOException, SQLException, HL7Exception { //make base directory if (!(baseDirectory.endsWith("\\") || baseDirectory.endsWith("/"))) { baseDirectory = baseDirectory + "/"; } File targetDir = SourceGenerator.makeDirectory(baseDirectory + DefaultModelClassFactory.getVersionPackagePath(version) + "segment"); ArrayList segments = getSegmentNames(version); if (segments.size() == 0) { log.warn("No version " + version + " segments found in database " + System.getProperty("ca.on.uhn.hl7.database.url")); } for (int i = 0; i < segments.size(); i++) { try { String seg = (String) segments.get(i); makeSegment(seg, version, theTemplatePackage, targetDir, theFileExt); } catch (Exception e) { System.err.println("Error creating source code for all segments: " + e.getMessage()); e.printStackTrace(); } } } public static ArrayList getSegmentNames(String version) throws SQLException { //get list of segments NormativeDatabase normativeDatabase = NormativeDatabase.getInstance(); Connection conn = normativeDatabase.getConnection(); Statement stmt = conn.createStatement(); String sql = "SELECT seg_code, section from HL7Segments, HL7Versions where HL7Segments.version_id = HL7Versions.version_id AND hl7_version = '" + version + "'"; //System.out.println(sql); ResultSet rs = stmt.executeQuery(sql); ArrayList segments = new ArrayList(); while (rs.next()) { String segName = rs.getString(1); // The DB has an invalid segment with this name if ("ED".equals(segName)) { continue; } if (Character.isLetter(segName.charAt(0))) { segments.add(altSegName(segName)); } } stmt.close(); normativeDatabase.returnConnection(conn); return segments; } /** *

Returns an alternate segment name to replace the given segment name. Substitutions * made include:

*
  • Replacing Z.. with Z
  • *
  • Replacing ??? with ???
*/ public static String altSegName(String segmentName) { String ret = segmentName; if (ret.equals("Z..")) { ret = "Z"; } if (ret.equals("CON")) { ret = "CON_"; } return ret; } /** * Returns the Java source code for a class that represents the specified segment. */ public static void makeSegment(String name, String version, String theTemplatePackage, File theTargetDir, String theFileExt) throws Exception { ArrayList elements = new ArrayList(); String segDesc = null; SegmentElement se = null; NormativeDatabase normativeDatabase = NormativeDatabase.getInstance(); try { Connection conn = normativeDatabase.getConnection(); StringBuffer sql = new StringBuffer(); sql.append("SELECT HL7SegmentDataElements.seg_code, HL7SegmentDataElements.seq_no, "); sql.append("HL7SegmentDataElements.repetitional, HL7SegmentDataElements.repetitions, "); sql.append("HL7DataElements.description, HL7DataElements.length, HL7DataElements.table_id, "); sql.append("HL7SegmentDataElements.req_opt, HL7Segments.description, HL7DataElements.data_structure "); sql.append("FROM HL7Versions RIGHT JOIN (HL7Segments INNER JOIN (HL7DataElements INNER JOIN HL7SegmentDataElements "); sql.append("ON (HL7DataElements.version_id = HL7SegmentDataElements.version_id) "); sql.append("AND (HL7DataElements.data_item = HL7SegmentDataElements.data_item)) "); sql.append("ON (HL7Segments.version_id = HL7SegmentDataElements.version_id) "); sql.append("AND (HL7Segments.seg_code = HL7SegmentDataElements.seg_code)) "); sql.append("ON (HL7Versions.version_id = HL7Segments.version_id) "); sql.append("WHERE HL7SegmentDataElements.seg_code = '"); sql.append(name); sql.append("' and HL7Versions.hl7_version = '"); sql.append(version); sql.append("' ORDER BY HL7SegmentDataElements.seg_code, HL7SegmentDataElements.seq_no;"); //System.out.println(sql.toString()); //for debugging Statement stmt = conn.createStatement(); ResultSet rs = stmt.executeQuery(sql.toString()); List usedFieldDescs = new ArrayList(); int index = 0; while (rs.next()) { if (segDesc == null) { segDesc = rs.getString(9); } se = new SegmentElement(name, version, index++); se.field = rs.getInt(2); se.rep = rs.getString(3); se.repetitions = rs.getInt(4); if (se.repetitions == 0) { if (se.rep == null || !se.rep.equalsIgnoreCase("Y")) { se.repetitions = 1; } } se.desc = rs.getString(5); // If two fields have the same name, add "Rep 1" or "Rep 2" etc to the name String originalSeDesc = se.desc; if (usedFieldDescs.contains(se.desc)) { se.desc = se.desc + " Number " + (Collections.frequency(usedFieldDescs, originalSeDesc) + 1); } usedFieldDescs.add(originalSeDesc); se.length = rs.getInt(6); se.table = rs.getInt(7); se.opt = rs.getString(8); se.type = rs.getString(10); //shorten CE_x to CE if (se.type.startsWith("CE")) { se.type = "CE"; } if (se.type.equals("-") || se.type.equals("NUL")) { se.type = "NULLDT"; } elements.add(se); /*System.out.println("Segment: " + name + " Field: " + se.field + " Rep: " + se.rep + " Repetitions: " + se.repetitions + " Desc: " + se.desc + " Length: " + se.length + " Table: " + se.table + " Segment Desc: " + segDesc);*/ } stmt.close(); normativeDatabase.returnConnection(conn); } catch (SQLException sqle) { sqle.printStackTrace(); return; } String fileName = theTargetDir.toString() + "/" + name + "." + theFileExt; String basePackageName = DefaultModelClassFactory.getVersionPackageName(version); String[] datatypePackages = { basePackageName + "datatype" }; writeSegment(fileName, version, name, elements, segDesc, basePackageName, datatypePackages, theTemplatePackage); } private static void makeFieldAccessor(String name, String version, StringBuffer source, SegmentElement se, String accessorName) { if (se.desc.equalsIgnoreCase("UNUSED")) { //some entries in 2.1 DB say "unused" return; } String type = SourceGenerator.getAlternateType(se.type, version); source.append(" /**\r\n"); source.append(" * Returns "); if (se.repetitions != 1) { source.append("a single repetition of "); } source.append(se.desc); source.append(" ("); source.append(name); source.append("-"); source.append(se.field); source.append(").\r\n"); if (se.repetitions != 1) { source.append(" * @param rep the repetition number (this is a repeating field)\r\n"); source.append(" * @throws HL7Exception if the repetition number is invalid.\r\n"); } source.append(" */\r\n"); source.append(" public "); source.append(type); source.append(" get"); source.append(accessorName); source.append("("); if (se.repetitions != 1) { source.append("int rep"); } source.append(") "); if (se.repetitions != 1) { source.append("throws HL7Exception"); } source.append(" {\r\n"); source.append(" "); source.append(type); source.append(" ret = null;\r\n"); source.append(" try {\r\n"); source.append(" Type t = this.getField("); source.append(se.field); source.append(", "); if (se.repetitions == 1) { source.append("0"); } else { source.append("rep"); } source.append(");\r\n"); source.append(" ret = ("); source.append(type); source.append(")t;\r\n"); source.append(" } catch (ClassCastException cce) {\r\n"); source.append(" HapiLogFactory.getHapiLog(this.getClass()).error(\"Unexpected problem obtaining field value. This is a bug.\", cce);\r\n"); source.append(" throw new RuntimeException(cce);\r\n"); if (se.repetitions == 1) { source.append(" } catch (HL7Exception he) {\r\n"); source.append(" HapiLogFactory.getHapiLog(this.getClass()).error(\"Unexpected problem obtaining field value. This is a bug.\", he);\r\n"); source.append(" throw new RuntimeException(he);\r\n"); } source.append(" }\r\n"); source.append(" return ret;\r\n"); source.append(" }\r\n\r\n"); //add an array accessor as well for repeating fields if (se.repetitions != 1) { source.append(" /**\r\n"); source.append(" * Returns all repetitions of "); source.append(se.desc); source.append(" ("); source.append(name); source.append("-"); source.append(se.field); source.append(").\r\n"); source.append(" */\r\n"); source.append(" public "); source.append(type); source.append("[] get"); source.append(accessorName); source.append("() {\r\n"); source.append(" "); source.append(type); source.append("[] ret = null;\r\n"); source.append(" try {\r\n"); source.append(" Type[] t = this.getField("); source.append(se.field); source.append("); \r\n"); source.append(" ret = new "); source.append(type); source.append("[t.length];\r\n"); source.append(" for (int i = 0; i < ret.length; i++) {\r\n"); source.append(" ret[i] = ("); source.append(type); source.append(")t[i];\r\n"); source.append(" }\r\n"); source.append(" } catch (ClassCastException cce) {\r\n"); source.append(" HapiLogFactory.getHapiLog(this.getClass()).error(\"Unexpected problem obtaining field value. This is a bug.\", cce);\r\n"); source.append(" throw new RuntimeException(cce);\r\n"); source.append(" } catch (HL7Exception he) {\r\n"); source.append(" HapiLogFactory.getHapiLog(this.getClass()).error(\"Unexpected problem obtaining field value. This is a bug.\", he);\r\n"); source.append(" throw new RuntimeException(he);\r\n"); source.append(" }\r\n"); source.append(" return ret;\r\n"); source.append(" }\r\n\r\n"); // Add count reps method source.append(" /**\r\n"); source.append(" * Returns a count of the number of repetitions of "); source.append(se.desc); source.append(" ("); source.append(name); source.append("-"); source.append(se.field); source.append(").\r\n"); source.append(" */\r\n"); source.append(" public int get"); source.append(accessorName); source.append("Reps() {\r\n"); source.append(" try {\r\n"); source.append(" return this.getField("); source.append(se.field); source.append(").length; \r\n"); source.append(" } catch (ClassCastException cce) {\r\n"); source.append(" HapiLogFactory.getHapiLog(this.getClass()).error(\"Unexpected problem obtaining field value. This is a bug.\", cce);\r\n"); source.append(" throw new RuntimeException(cce);\r\n"); source.append(" } catch (HL7Exception he) {\r\n"); source.append(" HapiLogFactory.getHapiLog(this.getClass()).error(\"Unexpected problem obtaining field value. This is a bug.\", he);\r\n"); source.append(" throw new RuntimeException(he);\r\n"); source.append(" }\r\n"); source.append(" }\r\n\r\n"); // Add insert repetition method source.append(" /**\r\n"); source.append(" * Inserts a repetition of "); source.append(se.desc); source.append(" ("); source.append(name); source.append("-"); source.append(se.field); source.append(") at a given index and returns it.\r\n"); source.append(" * @param index The index\r\n"); source.append(" */\r\n"); source.append(" public "); source.append(type); source.append(" insert"); source.append(accessorName); source.append("(int index) throws HL7Exception {\r\n"); source.append(" return ("); source.append(type); source.append(") super.insertRepetition("); source.append(se.field); source.append(", index);\r\n"); source.append(" }\r\n\r\n"); // Add remove repetition method source.append(" /**\r\n"); source.append(" * Removes a repetition of "); source.append(se.desc); source.append(" ("); source.append(name); source.append("-"); source.append(se.field); source.append(") at a given index and returns it.\r\n"); source.append(" * @param index The index\r\n"); source.append(" */\r\n"); source.append(" public "); source.append(type); source.append(" remove"); source.append(accessorName); source.append("(int index) throws HL7Exception {\r\n"); source.append(" return ("); source.append(type); source.append(") super.removeRepetition("); source.append(se.field); source.append(", index);\r\n"); source.append(" }\r\n\r\n"); } } public static void writeSegment(String fileName, String version, String segmentName, ArrayList elements, String description, String basePackage, String[] datatypePackages, String theTemplatePackage) throws Exception { log.info("Writing segment: " + fileName); BufferedWriter out = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(fileName, false), SourceGenerator.ENCODING)); theTemplatePackage = theTemplatePackage.replace(".", "/"); Template template = VelocityFactory.getClasspathTemplateInstance(theTemplatePackage + "/segment.vsm"); VelocityContext ctx = new VelocityContext(); ctx.put("segmentName", segmentName); ctx.put("typeDescription", description); ctx.put("basePackageName", basePackage); ctx.put("elements", elements); ctx.put("datatypePackages", datatypePackages); template.merge(ctx, out); // String string = createSegmentString(version, segmentName, elements, description, basePackage, datatypePackageString); // out.write(string); out.flush(); out.close(); } }