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 "SourceGenerator.java".  Description:
10   * "Manages automatic generation of HL7 API source code for all data types,
11   * segments, groups, and message structures"
12   *
13   * The Initial Developer of the Original Code is University Health Network. Copyright (C)
14   * 2001.  All Rights Reserved.
15   *
16   * Contributor(s): ______________________________________.
17   *
18   * Alternatively, the contents of this file may be used under the terms of the
19   * GNU General Public License (the  �GPL�), in which case the provisions of the GPL are
20   * applicable instead of those above.  If you wish to allow use of your version of this
21   * file only under the terms of the GPL and not to allow others to use your version
22   * of this file under the MPL, indicate your decision by deleting  the provisions above
23   * and replace  them with the notice and other provisions required by the GPL License.
24   * If you do not delete the provisions above, a recipient may use your version of
25   * this file under either the MPL or the GPL.
26   *
27   */
28  
29  package ca.uhn.hl7v2.sourcegen;
30  
31  import java.util.StringTokenizer;
32  import java.io.File;
33  import java.io.IOException;
34  
35  import ca.uhn.hl7v2.HL7Exception;
36  import org.apache.commons.lang.StringUtils;
37  
38  /**
39   * <p>Manages automatic generation of HL7 API source code for all data types,
40   * segments, groups, and message structures. </p>
41   * <p>Note: should put a nice UI on this</p>
42   * @author Bryan Tripp (bryan_tripp@sourceforge.net)
43   */
44  public class SourceGenerator {
45      
46  	public static final String ENCODING = "UTF-8";
47  	
48      /** Creates new SourceGenerator */
49      public SourceGenerator() {
50      }
51      
52      /**
53       * Generates source code for all data types, segments, groups, and messages.
54       * @param baseDirectory the directory where source should be written
55       * @throws HL7Exception - 
56       */
57      public static void makeAll(String baseDirectory, String version, boolean failOnError, String theTemplatePackage, String theFileExt) throws HL7Exception  {
58          //load driver and set DB URL
59          /*if (System.getProperty("ca.on.uhn.hl7.database.url") == null) {
60              System.setProperty("ca.on.uhn.hl7.database.url", "jdbc:odbc:hl7");
61          }*/
62          
63          try {
64              Class.forName("sun.jdbc.odbc.JdbcOdbcDriver");
65              MessageGenerator.makeAll(baseDirectory, version, failOnError, theTemplatePackage, theFileExt);
66              SegmentGenerator.makeAll(baseDirectory, version, theTemplatePackage, theFileExt);
67              DataTypeGenerator.makeAll(baseDirectory, version, theTemplatePackage, theFileExt);
68              // group and message not implemented
69          } catch (Exception e) {
70          	throw new HL7Exception(e);
71          }
72          
73      }
74      
75      /**
76       * Make a Java-ish accessor method name out of a field or component description
77       * by removing non-letters and adding "get".  One complication is that some description
78       * entries in the DB have their data types in brackets, and these should not be
79       * part of the method name.  On the other hand, sometimes critical distinguishing
80       * information is in brackets, so we can't omit everything in brackets.  The approach
81       * taken here is to eliminate bracketed text if a it looks like a data type.
82       */
83      public static String makeAccessorName(String fieldDesc, String parentName) {
84          StringBuilder aName = new StringBuilder();
85          char[] chars = fieldDesc.toCharArray();
86          boolean lastCharWasNotLetter = true;
87          int inBrackets = 0;
88          StringBuffer bracketContents = new StringBuffer();
89          for (char aChar : chars) {
90              if (aChar == '(') inBrackets++;
91              if (aChar == ')') inBrackets--;
92  
93              if (Character.isLetterOrDigit(aChar)) {
94                  if (inBrackets > 0) {
95                      //buffer everthing in brackets
96                      bracketContents.append(aChar);
97                  } else {
98                      //add capitalized bracketed text if appropriate
99                      if (bracketContents.length() > 0) {
100                         aName.append(capitalize(filterBracketedText(bracketContents.toString())));
101                         bracketContents = new StringBuffer();
102                     }
103                     if (lastCharWasNotLetter) {
104                         //first letter of each word is upper-case
105                         aName.append(Character.toUpperCase(aChar));
106                     } else {
107                         aName.append(aChar);
108                     }
109                     lastCharWasNotLetter = false;
110                 }
111             } else {
112                 lastCharWasNotLetter = true;
113             }
114         }
115         aName.append(capitalize(filterBracketedText(bracketContents.toString())));        
116         String retVal = aName.toString();
117 
118         // Accessors with these two names conflict with existing superclass accessor names
119         if (retVal.equals("Parent")) {
120             retVal = parentName + "Parent";
121         }
122         if (retVal.equals("Name")) {
123             retVal = parentName + "Name";
124         }
125         
126         return retVal;
127     }
128     
129     /**
130      * Bracketed text in a field description should be included in the accessor 
131      * name unless it corresponds to a data type name. Given the text that appears in 
132      * brackets in a field description, this method returns an empty string if it 
133      * corresponds to a data type name, or returns original text if not.  It isn't 
134      * convenient to actually check (e.g. with DataTypeGenerator) whether the given 
135      * text actually corresponds to a data type name, so we are going to conclude that 
136      * it is a data type if and only if it is all caps and has 2 or 3 characters.  
137      */
138     private static String filterBracketedText(String text) {
139         String filtered = "";
140         boolean isDataType = true;
141         if (!text.equals(text.toUpperCase())) isDataType = false;
142         if (text.length() < 2 || text.length() > 3) isDataType = false;
143 
144         if (!isDataType) filtered = text;
145         return filtered;
146     }
147     
148     /** Capitalizes first character of the given text. */
149     private static String capitalize(String text) {
150         StringBuilder cap = new StringBuilder();
151         if (text.length() > 0) {
152             cap.append(Character.toUpperCase(text.charAt(0)));
153             cap.append(text.substring(1));
154         }
155         return cap.toString();
156     }
157     
158     /**
159      * Creates the given directory if it does not exist.
160      */
161     public static File makeDirectory(String directory) throws IOException {
162         StringTokenizer tok = new StringTokenizer(directory, "\\/", false);
163         
164         File currDir = directory.startsWith("/") ? new File("/") : null;
165         while (tok.hasMoreTokens()) {
166             String thisDirName = tok.nextToken();
167             
168             currDir = new File(currDir, thisDirName); //if currDir null, defaults to 1 arg call
169             
170             if (!currDir.exists()) {
171                 //create
172                 currDir.mkdir();
173             } else if (currDir.isFile()) {
174                 throw new IOException("Can't create directory " + thisDirName +
175                 " - file with same name exists.");
176             }
177         }
178         
179         return currDir;
180     }
181     
182     /**
183      * <p>Returns either the given data type name or an alternate data type that Composites
184      * and Segments should use in place of the given Type.  </p>
185      * <p>As currently implemented, substitutions
186      * may be desired if there is a validating subclass of the given Type.
187      * By convention such classes would be named ValidX (where X is the Type name).  This
188      * method checks the classpath for classes of this name in the datatype package and
189      * returns this name if one is found.</p>
190      * <p>Also converts "varies" to Varies which is an implementation of Type that contains
191      * a Type that can be set at run-time.</p>
192      */
193     public static String getAlternateType(String dataTypeName, String version) {
194         String ret = dataTypeName;
195         
196         //convert to varies to Varies
197         if (ret.equals("varies")) ret = "Varies";
198         
199         // Null/withdrawn
200         if (ret.equals("NUL")) ret = "NULLDT";
201         if (ret.equals("-")) ret = "NULLDT";
202         
203         //Valid.. classes are removed as of HAPI 0.3 (validating code implemented directly in Primitive classes
204         /*try {
205             Class.forName(getVersionPackageName(version) + "datatype.Valid" + dataTypeName);
206             ret = "Valid" + dataTypeName;
207         } catch (Exception e) {
208             // fine ... there isn't a ValidX implementation
209             // I don't like using Class.forName here but I don't know a better way to
210             // search for the class 
211         }*/
212         
213         return ret;
214     }
215     
216     public static void main(String[] args) throws ClassNotFoundException, HL7Exception {
217     	Class.forName("com.mysql.jdbc.Driver");
218     	System.setProperty("ca.on.uhn.hl7.database.url", "jdbc:mysql://localhost:3306/hl7v65");
219         System.setProperty("ca.on.uhn.hl7.database.user", "hl7");
220         System.setProperty("ca.on.uhn.hl7.database.password", "hl7");
221         makeAll("tmp", "2.5.1", true, "", "java");
222     }
223 
224 	public static String makeAlternateAccessorName(String fieldDesc, String parentName, int index) {
225         StringBuilder aName = new StringBuilder();
226         
227         aName.append(StringUtils.capitalize(parentName.toLowerCase())).append(index).append("_");
228         
229         char[] chars = fieldDesc.toCharArray();
230         boolean lastCharWasNotLetter = true;
231         int inBrackets = 0;
232         StringBuffer bracketContents = new StringBuffer();
233         for (char aChar : chars) {
234             if (aChar == '(') inBrackets++;
235             if (aChar == ')') inBrackets--;
236 
237             if (Character.isLetterOrDigit(aChar)) {
238                 if (inBrackets > 0) {
239                     //buffer everthing in brackets
240                     bracketContents.append(aChar);
241                 } else {
242                     //add capitalized bracketed text if appropriate
243                     if (bracketContents.length() > 0) {
244                         aName.append(capitalize(filterBracketedText(bracketContents.toString())));
245                         bracketContents = new StringBuffer();
246                     }
247                     if (lastCharWasNotLetter) {
248                         //first letter of each word is upper-case
249                         aName.append(Character.toUpperCase(aChar));
250                     } else {
251                         aName.append(aChar);
252                     }
253                     lastCharWasNotLetter = false;
254                 }
255             } else {
256                 lastCharWasNotLetter = true;
257             }
258         }
259         aName.append(capitalize(filterBracketedText(bracketContents.toString())));
260 
261 
262         return aName.toString();
263 	}
264     
265 }