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 "XsdDataTypeGenerator.java".  Description:
10   "Create HAPI datatype model classes from XSD"
11  
12   The Initial Developer of the Original Code is University Health Network. Copyright (C)
13   2013.  All Rights Reserved.
14  
15   Contributor(s): ______________________________________.
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.*;
30  import java.net.URL;
31  import java.nio.charset.StandardCharsets;
32  import java.util.ArrayList;
33  import java.util.Arrays;
34  import java.util.Iterator;
35  import java.util.List;
36  
37  import ca.uhn.hl7v2.Version;
38  import ca.uhn.hl7v2.parser.DefaultModelClassFactory;
39  import ca.uhn.hl7v2.sourcegen.util.VelocityFactory;
40  import com.sun.xml.xsom.*;
41  import com.sun.xml.xsom.parser.XSOMParser;
42  import org.apache.velocity.Template;
43  import org.apache.velocity.VelocityContext;
44  import org.apache.velocity.context.Context;
45  
46  /**
47   * Create HAPI datatype model classes from XML Schema files. Download the files from
48   * http://www.hl7.org/documentcenter/private/standards/V26/HL7-xml_v2.6_annotated.zip and
49   * http://www.hl7.org/documentcenter/public/standards/V2/Sun_HL7v2xsd.zip
50   * and unpack into src/main/resources/hl7v2xsd.
51   * <p>
52   * This is an attempt to remove the need to have the HL7 database around because the
53   * schema files can be downloaded by any who has access to the HL7 standards.
54   */
55  public class XsdDataTypeGenerator {
56  
57      private static final String[] PRIMITIVES = {"FT", "GTS", "NM", "SI", "ST", "TN",  "TX"};
58      private static final String[] EXCLUDE_COMPOSITES = {"TX_CHALLENGE", "escapeType", "varies"};
59  
60      public static final String URN_HL7_ORG_V2XML = "urn:hl7-org:v2xml";
61  
62      private final String templatePackage;
63      private final String targetDirectory;
64  
65      public XsdDataTypeGenerator(String dir, String templatePackage) throws IOException {
66          File f = new File(dir);
67          if (!f.isDirectory())
68              throw new IOException("Can't create file in " +
69                      dir + " - it is not a directory.");
70          this.targetDirectory = dir;
71          this.templatePackage = templatePackage.replace(".", "/");
72      }
73  
74      public void parse(Version version) throws Exception {
75          XSOMParser parser = new XSOMParser();
76          String dir = String.format("/hl7v2xsd/%s/datatypes.xsd", version.getVersion());
77          URL url = getClass().getResource(dir);
78          parser.parse(url);
79          XSSchemaSet result = parser.getResult();
80          Iterator<XSSchema> iter = result.iterateSchema();
81          while (iter.hasNext()) {
82              XSSchema schema = iter.next();
83              if (URN_HL7_ORG_V2XML.equals(schema.getTargetNamespace())) {
84                  parsePrimitives(schema, version);
85                  parseComposites(schema, version);
86              }
87          }
88      }
89  
90      private void parsePrimitives(XSSchema schema, Version version) throws Exception {
91          List<DatatypeDef> primitiveTypes = new ArrayList<>();
92          Iterator<XSType> types = schema.iterateTypes();
93  
94          while (types.hasNext()) {
95              XSType type = types.next();
96              String dataTypeName = type.getName();
97              if (Arrays.binarySearch(PRIMITIVES, dataTypeName) >= 0)
98                  primitiveTypes.add(new DatatypeDef(dataTypeName, dataTypeName));
99          }
100 
101         Template template = VelocityFactory.getClasspathTemplateInstance(templatePackage + "/datatype_primitive.vsm");
102         String basePackageName = DefaultModelClassFactory.getVersionPackageName(version.getVersion());
103         String normalBasePackageName = DefaultModelClassFactory.getVersionPackageName(version.getVersion());
104 
105         for (DatatypeDef def : primitiveTypes) {
106             String source = make(template, basePackageName, normalBasePackageName, def, version.getVersion());
107             if (source != null) {
108                 writeFile(source, def, version);
109             }
110         }
111     }
112 
113     private void parseComposites(XSSchema schema, Version version) throws Exception {
114         List<DatatypeDef> compositeTypes = new ArrayList<>();
115         Iterator<XSComplexType> types = schema.iterateComplexTypes();
116         while (types.hasNext()) {
117             XSComplexType complexType = types.next();
118             String dataTypeName = complexType.getName();
119 
120             // Omit CE_X types
121             if (dataTypeName.startsWith("CE_")) dataTypeName = "CE";
122 
123             if (isRealComposite(dataTypeName)) {
124                 DatatypeDefDatatypeDef">DatatypeDef compositeType = new DatatypeDef(dataTypeName, dataTypeName);
125                 // Extract list of components from the composite type
126                 XSParticle[] children = complexType
127                         .getContentType()
128                         .asParticle()
129                         .getTerm()
130                         .asModelGroup()
131                         .getChildren();
132                 // Iterate over all components
133                 for (int i = 0; i < children.length; i++) {
134                     XSAttGroupDecl attrGroup = children[i]
135                             .getTerm()
136                             .asElementDecl()
137                             .getType()
138                             .asComplexType()
139                             .getAttGroups().iterator().next();
140                     String componentType = attrGroup.getAttributeUse("", "Type").getFixedValue().toString();
141                     String componentDescription = attrGroup.getAttributeUse("", "LongName").getFixedValue().toString();
142                     XSAttributeUse componentTable = attrGroup.getAttributeUse("", "Table");
143                     int table = 0;
144                     if (componentTable != null)
145                         table = Integer.parseInt(componentTable.getFixedValue().toString().substring(3));
146 
147                     compositeType.addSubcomponentDef(
148                             new DatatypeComponentDef(
149                                     dataTypeName,
150                                     i,
151                                     fixTypeName(dataTypeName, componentType),
152                                     componentDescription,
153                                     table));
154                 }
155                 compositeTypes.add(compositeType);
156             }
157 
158         }
159 
160         Template template = VelocityFactory.getClasspathTemplateInstance(templatePackage + "/datatype_composite.vsm");
161         String basePackageName = DefaultModelClassFactory.getVersionPackageName(version.getVersion());
162         String normalBasePackageName = DefaultModelClassFactory.getVersionPackageName(version.getVersion());
163 
164         for (DatatypeDef def : compositeTypes) {
165             String source = make(template, basePackageName, normalBasePackageName, def, version.getVersion());
166             if (source != null) {
167                 writeFile(source, def, version);
168             }
169         }
170     }
171 
172     // Really need all of this?
173     private static String fixTypeName(String parentName, String dataTypeName) {
174         if (dataTypeName.equals("ST") && parentName.equals("TS")) return "TSComponentOne";
175         //convert to varies to Varies
176         if (dataTypeName.equals("varies")) return "Varies";
177 
178         // Null/withdrawn
179         if (dataTypeName.equals("NUL")) return "NULLDT";
180         if (dataTypeName.equals("-")) return "NULLDT";
181 
182         return dataTypeName;
183     }
184 
185     private boolean isRealComposite(String dataTypeName) {
186         return Arrays.binarySearch(PRIMITIVES, dataTypeName) < 0 &&
187                 Arrays.binarySearch(EXCLUDE_COMPOSITES, dataTypeName) < 0 &&
188                 dataTypeName.indexOf('.') < 0;
189     }
190 
191     private void writeFile(String source, DatatypeDef def, Version version) throws IOException {
192         // TODO must be more robust
193         String dirName = String.format("%s/ca/uhn/hl7v2/model/%s/datatype/", targetDirectory, version.getPackageVersion());
194         File dir = new File(dirName);
195         if (!dir.exists()) {
196             dir.mkdirs();
197         }
198         String targetFile = String.format("%s/%s.java", dirName, def.getType());
199 
200         try (BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(targetFile, false), StandardCharsets.UTF_8))) {
201             writer.write(source);
202             writer.flush();
203         }
204     }
205 
206     private String make(Template template, String basePackageName, String normalBasePackageName,
207                         DatatypeDef def, String version) {
208         StringWriter out = new StringWriter();
209         Context ctx = new VelocityContext();
210         ctx.put("datatype", def);
211         ctx.put("datatypeName", def.getType());
212         ctx.put("version", version);
213         ctx.put("basePackageName", basePackageName);
214         ctx.put("normalBasePackageName", normalBasePackageName);
215         ctx.put("components", def.getSubComponentDefs());
216         ctx.put("desc", def.getName());
217 
218         template.merge(ctx, out);
219         return out.toString();
220     }
221 
222 
223     public static void main(String... args) {
224         try {
225             XsdDataTypeGeneratortor.html#XsdDataTypeGenerator">XsdDataTypeGenerator xdtg = new XsdDataTypeGenerator("C:/temp", "/ca.uhn.hl7v2.sourcegen.templates");
226             long start = System.currentTimeMillis();
227             for (Version version : Version.values()) {
228                 System.out.println("Creating data types for " + version);
229                 xdtg.parse(version);
230             }
231             System.out.println("Done in " + (System.currentTimeMillis() - start) + " ms.");
232         } catch (Exception e) {
233             e.printStackTrace();
234         }
235     }
236 }