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
29 package ca.uhn.hl7v2.mvnplugin;
30
31 import java.io.File;
32 import java.util.ArrayList;
33 import java.util.Arrays;
34 import java.util.Collections;
35 import java.util.HashMap;
36 import java.util.List;
37 import java.util.Map;
38 import java.util.Set;
39 import java.util.TreeMap;
40
41 import org.apache.maven.plugin.AbstractMojo;
42 import org.apache.maven.plugin.MojoExecutionException;
43 import org.apache.maven.plugin.MojoFailureException;
44 import org.apache.maven.plugins.annotations.Component;
45 import org.apache.maven.plugins.annotations.LifecyclePhase;
46 import org.apache.maven.plugins.annotations.Mojo;
47 import org.apache.maven.plugins.annotations.Parameter;
48 import org.apache.maven.plugins.annotations.ResolutionScope;
49 import org.apache.maven.project.MavenProject;
50 import org.springframework.beans.factory.config.BeanDefinition;
51 import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
52 import org.springframework.core.io.DefaultResourceLoader;
53 import org.springframework.core.type.filter.AssignableTypeFilter;
54
55 import ca.uhn.hl7v2.HL7Exception;
56 import ca.uhn.hl7v2.Version;
57 import ca.uhn.hl7v2.model.GenericComposite;
58 import ca.uhn.hl7v2.model.Group;
59 import ca.uhn.hl7v2.model.Message;
60 import ca.uhn.hl7v2.parser.DefaultModelClassFactory;
61 import ca.uhn.hl7v2.sourcegen.GroupDef;
62 import ca.uhn.hl7v2.sourcegen.GroupGenerator;
63 import ca.uhn.hl7v2.sourcegen.MessageGenerator;
64 import ca.uhn.hl7v2.sourcegen.SegmentDef;
65 import ca.uhn.hl7v2.sourcegen.StructureDef;
66 import ca.uhn.hl7v2.util.ReflectionUtil;
67
68
69
70
71
72
73 @Mojo(name = "superstructuregen",
74 defaultPhase = LifecyclePhase.GENERATE_SOURCES,
75 requiresDependencyCollection = ResolutionScope.COMPILE_PLUS_RUNTIME,
76 requiresProject = true,
77 inheritByDefault = false)
78 public class SuperStructureMojo extends AbstractMojo {
79
80
81
82
83 @Component
84 private MavenProject project;
85
86
87
88
89 @Parameter(required = false)
90 private boolean skip;
91
92
93
94
95 @Parameter(required = false)
96 private List<String> structures;
97
98
99
100
101
102 @Parameter(required = false)
103 private String targetDirectory;
104
105
106
107
108 @Parameter(required = false)
109 private String targetStructureName;
110
111 private final String templatePackage = "ca.uhn.hl7v2.sourcegen.templates";
112
113
114
115
116 @Parameter(required = false)
117 private String version;
118
119
120
121
122 @Override
123 public void execute() throws MojoFailureException {
124
125 if (skip) {
126 getLog().warn("Configured to skip");
127 }
128
129 try {
130 List<String> allStructures = new ArrayList<>();
131 DefaultModelClassFactory mcf = new DefaultModelClassFactory();
132
133
134 Version versionOf = Version.versionOf(version);
135 if (versionOf == null) {
136 throw new MojoExecutionException("Unknown version: " + version);
137 }
138
139
140
141
142
143
144
145
146
147
148
149 ClassPathScanningCandidateComponentProvider scanner = new ClassPathScanningCandidateComponentProvider(true);
150 DefaultResourceLoader resourceLoader = new DefaultResourceLoader(GenericComposite.class.getClassLoader());
151 scanner.setResourceLoader(resourceLoader);
152 scanner.addIncludeFilter(new AssignableTypeFilter(Message.class));
153 Set<BeanDefinition> components = scanner.findCandidateComponents("ca/uhn/hl7v2/model/" + versionOf.getPackageVersion() + "/message");
154 for (BeanDefinition beanDefinition : components) {
155 String nextName = Class.forName(beanDefinition.getBeanClassName()).getSimpleName();
156 if (nextName.equals(targetStructureName)) {
157 continue;
158 }
159 allStructures.add(nextName);
160 }
161
162 getLog().info("Found " + allStructures.size() + " message classes for version: " + version);
163
164 List<Message> messagesToMerge = new ArrayList<>();
165
166 Collections.sort(allStructures);
167 for (String nextStructure : allStructures) {
168 for (String nextStructureToMerge : structures) {
169 if (nextStructure.matches(nextStructureToMerge)) {
170 Class<? extends Message> clazz = mcf.getMessageClass(nextStructure, version, true);
171 messagesToMerge.add(ReflectionUtil.instantiateMessage(clazz, mcf));
172 }
173 }
174 }
175
176 if (messagesToMerge.isEmpty()) {
177 throw new MojoFailureException("No messages match pattern(s): " + structures);
178 }
179
180 ListOfStructureDefsAndMapOfStructreNames mergedMessages = mergeGroups(messagesToMerge, messagesToMerge);
181 List<StructureDef> structures = mergedMessages.myStructureDefs;
182 if (structures.isEmpty()) {
183 throw new MojoExecutionException("No structures found matching structures to merge");
184 }
185
186 getLog().info("Creating directory: " + targetDirectory);
187 new File(targetDirectory).mkdirs();
188
189 boolean haveGroups = false;
190 for (StructureDef structureDef : structures) {
191 if (structureDef.isGroup()) {
192 haveGroups = true;
193 writeGroup((GroupDef) structureDef);
194 }
195 }
196
197 String fileName = MessageGenerator.determineTargetDir(targetDirectory + "/", version) + "/" + targetStructureName + ".java";
198 getLog().info("Filename will be: " + fileName);
199
200 StructureDef[] contents = structures.toArray(new StructureDef[0]);
201 String basePackageName = DefaultModelClassFactory.getVersionPackageName(version);
202 MessageGenerator.writeMessage(fileName, contents, targetStructureName, "", version, basePackageName, haveGroups, templatePackage, mergedMessages.myStructureNameToChildNames);
203
204 } catch (Exception e) {
205 throw new MojoFailureException("Failed to generate structure", e);
206 }
207 getLog().info("Adding " + targetDirectory + " to compile source root");
208 project.addCompileSourceRoot(targetDirectory);
209
210 }
211
212 private void writeGroup(GroupDef theStructureDef) throws Exception {
213
214 StructureDef[] structures = theStructureDef.getStructures();
215 String groupName = theStructureDef.getUnqualifiedName();
216 GroupGenerator.writeGroup(structures, groupName, targetDirectory, version, targetStructureName, templatePackage, "java");
217
218 for (StructureDef structureDef : structures) {
219 if (structureDef instanceof GroupDef) {
220 writeGroup((GroupDef) structureDef);
221 }
222 }
223
224 }
225
226 private static class ListOfStructureDefsAndMapOfStructreNames {
227 private List<StructureDef> myStructureDefs;
228 private Map<String, List<String>> myStructureNameToChildNames;
229 }
230
231 private ListOfStructureDefsAndMapOfStructreNames mergeGroups(List<? extends Group> theGroupsToMerge, List<Message> theAssociatedStructures) throws HL7Exception, MojoFailureException {
232 ArrayList<StructureDef> retValStructureDefs = new ArrayList<>();
233
234 List<List<String>> allNameLists = new ArrayList<>();
235 for (Group nextGroup : theGroupsToMerge) {
236 List<String> nextList = Arrays.asList(nextGroup.getNames());
237
238
239
240
241
242 allNameLists.add(nextList);
243 }
244
245 ArrayList<String> structureNames = mergeStringLists(allNameLists);
246 int currentStructureIdx = 0;
247 for (String nextStructureName : structureNames) {
248
249
250
251
252
253
254
255 if (structureNames.subList(0, currentStructureIdx++).contains(nextStructureName)) {
256 continue;
257 }
258
259 boolean required = true;
260 boolean repeating = false;
261 boolean group = false;
262 boolean choice = false;
263 List<Group> childGroups = new ArrayList<>();
264 List<Message> associatedChildStructures = new ArrayList<>();
265
266 int idx = 0;
267 for (Group nextGroup : theGroupsToMerge) {
268 if (Arrays.asList(nextGroup.getNames()).contains(nextStructureName)) {
269
270 if (theAssociatedStructures != null) {
271 associatedChildStructures.add(theAssociatedStructures.get(idx));
272 }
273
274 repeating |= nextGroup.isRepeating(nextStructureName);
275 choice |= nextGroup.isChoiceElement(nextStructureName);
276 required &= nextGroup.isRequired(nextStructureName);
277 if (nextGroup.isGroup(nextStructureName)) {
278 group = true;
279 childGroups.add((Group) nextGroup.get(nextStructureName));
280 }
281 } else {
282 required = false;
283 }
284
285 idx++;
286 }
287
288 Version versionEnum = Version.versionOf(version);
289 if (versionEnum == null) {
290 throw new MojoFailureException("Invalid version: " + version);
291 }
292 Map<String, String> eventMapForVersion = new DefaultModelClassFactory().getEventMapForVersion(versionEnum);
293 if (eventMapForVersion == null) {
294 throw new MojoFailureException("No event map for version: " + version);
295 }
296 if (!group) {
297 SegmentDef seg = new SegmentDef(nextStructureName.substring(0, 3), "", required, repeating, choice, "");
298 retValStructureDefs.add(seg);
299
300
301
302
303
304
305 for (Message next : associatedChildStructures) {
306 seg.addAssociatedStructure(next.getName());
307 Map<String, String> evtMap = new TreeMap<>(eventMapForVersion);
308 for (Map.Entry<String, String> nextEntry : evtMap.entrySet()) {
309 String value = nextEntry.getValue();
310 String name = next.getName();
311 if (value.equals(name)) {
312 seg.addAssociatedStructure(nextEntry.getValue());
313 seg.addAssociatedStructure(nextEntry.getKey());
314 }
315 }
316 }
317
318 seg.setIndexName(nextStructureName);
319
320 continue;
321 }
322
323 GroupDef grp = new GroupDef(targetStructureName, nextStructureName, required, repeating, "");
324 grp.setIndexName(nextStructureName);
325 List<StructureDef> children = mergeGroups(childGroups, null).myStructureDefs;
326
327
328
329
330
331
332 for (Message next : associatedChildStructures) {
333 Map<String, String> evtMap = eventMapForVersion;
334 for (Map.Entry<String, String> nextEntry : evtMap.entrySet()) {
335 if (nextEntry.getValue().equals(next.getName())) {
336 grp.addAssociatedStructure(nextEntry.getKey());
337 }
338 }
339 }
340
341 for (StructureDef structureDef : children) {
342 grp.addStructure(structureDef);
343 }
344 retValStructureDefs.add(grp);
345
346 }
347
348 ListOfStructureDefsAndMapOfStructreNames retVal = new ListOfStructureDefsAndMapOfStructreNames();
349 retVal.myStructureDefs = retValStructureDefs;
350
351 if (theAssociatedStructures != null) {
352 HashMap<String, List<String>> retValMap = new HashMap<>();
353 for (Message next : theAssociatedStructures) {
354 retValMap.put(next.getName(), Arrays.asList(next.getNames()));
355 }
356 retVal.myStructureNameToChildNames = retValMap;
357 }
358
359 return retVal;
360 }
361
362 ArrayList<String> mergeStringLists(List<List<String>> allNameLists) {
363 ArrayList<String> baseList = new ArrayList<>(allNameLists.remove(0));
364 getLog().debug("Base list is: "+ baseList);
365
366 for (List<String> nextCompareList : allNameLists) {
367
368 getLog().debug("Next compare list: "+ nextCompareList);
369
370 int baseIndex = 0;
371 int compareIndex = 0;
372
373 while (compareIndex < nextCompareList.size()) {
374
375 String currentBase = baseList.get(baseIndex);
376 String currentCompare = nextCompareList.get(compareIndex);
377 if (currentBase.equals(currentCompare)) {
378 if (baseIndex + 1 < baseList.size()) {
379 baseIndex++;
380 }
381 compareIndex++;
382 continue;
383 }
384
385 List<String> subList = baseList.subList(baseIndex, baseList.size());
386
387
388 List<String> toAdd = null;
389 for (int searchCompareIndex = compareIndex + 1; searchCompareIndex < nextCompareList.size(); searchCompareIndex++) {
390 String find = nextCompareList.get(searchCompareIndex);
391 int foundAt = subList.indexOf(find);
392 if (foundAt != -1) {
393 toAdd = nextCompareList.subList(compareIndex, searchCompareIndex);
394 break;
395 }
396 }
397
398 int addAtIndex = baseIndex;
399
400 if (toAdd == null) {
401
402 toAdd = nextCompareList.subList(compareIndex, nextCompareList.size());
403 addAtIndex = baseList.size();
404
405 int foundInBaseAtIndex = subList.indexOf(currentCompare);
406 if (foundInBaseAtIndex != -1) {
407 baseIndex += foundInBaseAtIndex;
408 compareIndex++;
409 continue;
410 }
411
412 } else {
413
414 int foundInBaseAtIndex = subList.indexOf(currentCompare);
415 if (foundInBaseAtIndex != -1) {
416 baseIndex += foundInBaseAtIndex;
417
418 continue;
419 }
420
421 }
422
423 baseList.addAll(addAtIndex, toAdd);
424 baseIndex += toAdd.size();
425 compareIndex += toAdd.size();
426
427
428
429
430 }
431 getLog().debug("Base list is now: "+ baseList);
432
433 }
434
435 getLog().debug("Merged name list: "+ baseList);
436 return baseList;
437 }
438
439 public static void main(String[] args) throws MojoExecutionException, MojoFailureException {
440
441 SuperStructureMojo m = new SuperStructureMojo();
442 m.structures = new ArrayList<>();
443 m.structures.add("ADT_A[0-9]{2}");
444
445 m.targetDirectory = "target/merge";
446 m.targetStructureName = "ADT_AXX";
447 m.version = "2.1";
448 m.execute();
449 }
450
451 }