View Javadoc
1   package xuml.tools.model.compiler;
2   
3   import static xuml.tools.model.compiler.Util.getClasses;
4   
5   import java.io.ByteArrayOutputStream;
6   import java.io.File;
7   import java.io.FileNotFoundException;
8   import java.io.FileOutputStream;
9   import java.io.IOException;
10  import java.io.PrintStream;
11  import java.math.BigInteger;
12  import java.util.List;
13  import java.util.stream.Collectors;
14  import java.util.stream.Stream;
15  
16  import javax.persistence.EntityManager;
17  import javax.persistence.EntityManagerFactory;
18  
19  import com.google.common.base.Optional;
20  import com.google.common.base.Preconditions;
21  import com.google.common.collect.Lists;
22  
23  import jakarta.xml.bind.JAXBElement;
24  import xuml.tools.miuml.metamodel.jaxb.Class;
25  import xuml.tools.miuml.metamodel.jaxb.Domains;
26  import xuml.tools.miuml.metamodel.jaxb.LocalEffectiveSignalingEvent;
27  import xuml.tools.miuml.metamodel.jaxb.ModeledDomain;
28  import xuml.tools.miuml.metamodel.jaxb.Subsystem;
29  import xuml.tools.miuml.metamodel.jaxb.SubsystemElement;
30  import xuml.tools.model.compiler.runtime.CreationEvent;
31  import xuml.tools.model.compiler.runtime.Entity;
32  import xuml.tools.model.compiler.runtime.Event;
33  import xuml.tools.model.compiler.runtime.QueuedSignal;
34  import xuml.tools.model.compiler.runtime.SignalProcessorListenerFactory;
35  import xuml.tools.model.compiler.runtime.Signaller;
36  
37  /**
38   * Generates code associated with one modeled domain.
39   * 
40   * @author dxm
41   * 
42   */
43  public class CodeGeneratorJava {
44  
45      private final ModeledDomain domain;
46      private final String domainPackageName;
47      private final String domainSchema;
48      private final Domains domains;
49      private final File resourcesDirectory;
50      private final boolean generatePersistenceXml;
51      private final NameManager nameManager;
52      private final File entitySourceDirectory;
53      private final String implementationPackageName;
54      private final File implementationSourceDirectory;
55      private final boolean overwriteImplementation;
56  
57      public CodeGeneratorJava(Domains domains, String domainName, String domainPackageName,
58              String domainSchema, File entitySourceDirectory, File resourcesDirectory,
59              String implementationPackageName, File implementationSourceDirectory,
60              boolean generatePersistenceXml, boolean overwriteImplementation) {
61          Preconditions.checkNotNull(domains);
62          Preconditions.checkNotNull(domainName);
63          Preconditions.checkNotNull(domainPackageName);
64          Preconditions.checkNotNull(domainSchema);
65          Preconditions.checkNotNull(entitySourceDirectory);
66          Preconditions.checkNotNull(resourcesDirectory);
67          Preconditions.checkNotNull(implementationPackageName);
68          Preconditions.checkNotNull(implementationSourceDirectory);
69  
70          this.domains = domains;
71          this.entitySourceDirectory = entitySourceDirectory;
72          this.resourcesDirectory = resourcesDirectory;
73          this.implementationPackageName = implementationPackageName;
74          this.implementationSourceDirectory = implementationSourceDirectory;
75          this.generatePersistenceXml = generatePersistenceXml;
76          this.overwriteImplementation = overwriteImplementation;
77          this.domain = Util.getModeledDomain(domains, domainName);
78          this.domainPackageName = domainPackageName;
79          this.domainSchema = domainSchema;
80          this.nameManager = new NameManager();
81      }
82  
83      public static Builder builder() {
84          return new Builder();
85      }
86  
87      public void generate() {
88          generateEntitySources();
89      }
90  
91      private void generateEntitySources() {
92          log("generating " + entitySourceDirectory);
93          ModeledDomain md = domain;
94          Lookupsups.html#Lookups">Lookups lookups = new Lookups(domains, md);
95          for (Class cls : getClasses(md)) {
96              createEntityJavaSource(cls, entitySourceDirectory, lookups);
97              // createImplementationJavaSource(cls,
98              // implementationSourceDirectory,
99              // lookups);
100         }
101         createStateMachineTables(getClasses(md),
102                 new File(resourcesDirectory, "state-transitions.html"));
103         if (generatePersistenceXml)
104             createPersistenceXml(domain, new File(resourcesDirectory, "META-INF/persistence.xml"));
105         createContext(domain, entitySourceDirectory, lookups);
106         log("finished generation");
107     }
108 
109     private static void createStateMachineTables(List<Class> classes, File file) {
110         file.getParentFile().mkdirs();
111         try (PrintStream out = new PrintStream(file)) {
112             out.println("<html>");
113             out.println("<head>");
114             out.println("<style>");
115             out.println("table, th, td {\n" + "    border: 1px solid black;\n"
116                     + "    border-collapse: collapse;\n" + "}\n" + "th, td {\n"
117                     + "    padding: 15px;\n" + "}");
118             out.println("</style>");
119             out.println("</head>");
120             out.println("<body>");
121             for (Class cls : classes) {
122                 createStateMachineTable(cls, out);
123             }
124             out.println("</body>");
125             out.println("</html>");
126         } catch (FileNotFoundException e) {
127             throw new RuntimeException(e);
128         }
129     }
130 
131     private static void createStateMachineTable(Class cls, PrintStream out) {
132         if (cls.getLifecycle() == null)
133             return;
134         List<String> states = cls.getLifecycle().getState().stream().map(state -> state.getName())
135                 .sorted().collect(Collectors.toList());
136         out.println();
137         out.format("<h2>%s</h2>\n", cls.getName());
138         out.format("<table>\n");
139         out.format("<tr><th></th>%s</tr>",
140                 states.stream().map(s -> "<th>" + s + "</th>").collect(Collectors.joining()));
141         for (String state1 : states) {
142             out.format("<tr><th>%s</th>", state1);
143             for (String state2 : states) {
144                 String eventNames = cls.getLifecycle().getTransition().stream()
145                         .filter(t -> t.getState().equals(state1)
146                                 && t.getDestination().equals(state2))
147                         .map(t -> t.getEventID()).map(eventId -> eventName(cls, eventId))
148                         .collect(Collectors.joining("<br/>"));
149                 out.format("<td>%s</td>", eventNames);
150             }
151             out.println("</tr>");
152         }
153         out.println("</table>");
154     }
155 
156     private static String eventName(Class cls, BigInteger eventId) {
157         return cls.getLifecycle().getEvent().stream().flatMap(event -> {
158             if (event.getValue() instanceof xuml.tools.miuml.metamodel.jaxb.CreationEvent) {
159                 xuml.tools.miuml.metamodel.jaxb.CreationEvent creation = (xuml.tools.miuml.metamodel.jaxb.CreationEvent) event
160                         .getValue();
161                 if (creation.getID().equals(eventId))
162                     return Stream.of(creation.getName());
163                 else
164                     return Stream.empty();
165             } else if (event.getValue() instanceof LocalEffectiveSignalingEvent) {
166                 LocalEffectiveSignalingEvents/miuml/metamodel/jaxb/LocalEffectiveSignalingEvent.html#LocalEffectiveSignalingEvent">LocalEffectiveSignalingEvent local = (LocalEffectiveSignalingEvent) event
167                         .getValue();
168                 if (local.getID().equals(eventId))
169                     return Stream.of(local.getName());
170                 else
171                     return Stream.empty();
172             } else
173                 return Stream.empty();
174         }).findAny().orElseThrow(RuntimeException::new);
175     }
176 
177     private void createImplementationJavaSource(Class cls, File destination, Lookups lookups) {
178         ClassInfo info = createClassInfo(cls);
179         if (info.hasBehaviour()) {
180             log("generating " + getFullClassImplementationName(cls));
181             BehaviourImplementationWriterionWriter.html#BehaviourImplementationWriter">BehaviourImplementationWriter w = new BehaviourImplementationWriter(info,
182                     getFullClassImplementationName(cls));
183             String java = w.generate();
184             File file = new File(destination, getClassImplementationFilename(cls));
185             if (!file.exists() || overwriteImplementation)
186                 writeToFile(java.getBytes(), file);
187         }
188     }
189 
190     private String getClassImplementationFilename(Class cls) {
191         String s = getFullClassImplementationName(cls);
192         return s.replace(".", "/") + ".java";
193     }
194 
195     private String getFullClassImplementationName(Class cls) {
196         return implementationPackageName + "." + getClassJavaSimpleName(cls) + "Behaviour";
197     }
198 
199     private void createPersistenceXml(ModeledDomain domain, File file) {
200         try {
201             file.getParentFile().mkdirs();
202             try (FileOutputStream out = new FileOutputStream(file)) {
203                 String xml = generatePersistenceXml(domain);
204                 out.write(xml.toString().getBytes());
205             }
206         } catch (IOException e) {
207             throw new RuntimeException(e);
208         }
209     }
210 
211     private String generatePersistenceXml(ModeledDomain domain) {
212         List<String> classes = Lists.newArrayList();
213         for (Class cls : getClasses(domain)) {
214             ClassInfo info = createClassInfo(cls);
215             classes.add(info.getClassFullName());
216         }
217         classes.add(QueuedSignal.class.getName());
218         String xml = new PersistenceXmlWriter().generate(classes);
219         return xml;
220     }
221 
222     private void createContext(ModeledDomain domain, File destination, Lookups lookups) {
223         ByteArrayOutputStream bytes = new ByteArrayOutputStream();
224         PrintStream out = new PrintStream(bytes);
225 
226         TypeRegisterister.html#TypeRegister">TypeRegister types = new TypeRegister();
227         out.format("public class Context {\n\n");
228         out.format("    private static volatile %s signaller;\n\n", types.addType(Signaller.class));
229         out.format("    public static int sendSignalsInQueue() {\n");
230         out.format("        return signaller.sendSignalsInQueue();\n");
231         out.format("    }\n\n");
232         out.format("    public static long queueSize() {\n");
233         out.format("        return signaller.queueSize();\n");
234         out.format("    }\n\n");
235         out.format("    public static %s<%s> queuedSignals() {\n", types.addType(List.class),
236                 types.addType(QueuedSignal.class));
237         out.format("        return signaller.queuedSignals();\n");
238         out.format("    }\n\n");
239         out.format(
240                 "    public static <T extends %s<T>> String persistSignal(String fromEntityUniqueId, Object id, Class<T> cls, %s<T> event, long time, %s<Long> repeatIntervalMs, String entityUniqueId) {\n",
241                 types.addType(Entity.class), types.addType(Event.class),
242                 types.addType(Optional.class));
243         out.format(
244                 "        return signaller.persistSignal(fromEntityUniqueId, id, cls, event, time, repeatIntervalMs, entityUniqueId);\n");
245         out.format("    }\n\n");
246         out.format("    public synchronized static void stop() {\n");
247         out.format("        if (signaller != null) {\n");
248         out.format("            signaller.stop();\n");
249         out.format("        }\n");
250         out.format("    }\n\n");
251         out.format("    public static <T extends %s<T>> T create(%s<T> cls, %s<T> event) {\n",
252                 types.addType(Entity.class), types.addType(java.lang.Class.class),
253                 types.addType(CreationEvent.class));
254         out.format("        return signaller.create(cls,event);\n");
255         out.format("    }\n\n");
256         out.format(
257                 "    public synchronized static void setEntityManagerFactory(%s emf, String entityActorPoolSizeProperty) {\n",
258                 types.addType(EntityManagerFactory.class));
259         out.format(
260                 "        int entityActorPoolSize = %s.parseInt((String) emf.getProperties().get(entityActorPoolSizeProperty));\n",
261                 types.addType(Integer.class));
262         out.format("        setEntityManagerFactory(emf, entityActorPoolSize);\n",
263                 types.addType(Signaller.class), types.addType(Signaller.class));
264         out.format("    }\n\n");
265 
266         out.format(
267                 "    public synchronized static void setEntityManagerFactory(%s emf, int entityActorPoolSize) {\n",
268                 types.addType(EntityManagerFactory.class));
269         out.format("        signaller = new %s(emf, entityActorPoolSize, listenerFactory);\n",
270                 types.addType(Signaller.class), types.addType(Signaller.class));
271         for (
272 
273         Subsystem subsystem : domain.getSubsystem())
274 
275         {
276             for (JAXBElement<? extends SubsystemElement> element : subsystem
277                     .getSubsystemElement()) {
278                 if (element.getValue() instanceof Class) {
279                     Class cls = (Class) element.getValue();
280                     // create classes (impls)
281                     ClassInfo info = createClassInfo(cls);
282                     if (info.hasBehaviour())
283                         out.format("        %s.setSignaller_(signaller);\n",
284                                 types.addType(info.getClassFullName()));
285                 }
286             }
287         }
288         out.format("    }\n\n");
289 
290         out.format("    private static %s listenerFactory;\n\n",
291                 types.addType(SignalProcessorListenerFactory.class));
292         out.format("    public static void setEntityActorListenerFactory(%s listenerFactory) {\n",
293                 types.addType(SignalProcessorListenerFactory.class));
294         out.format("        if (signaller !=null)\n");
295         out.format(
296                 "            throw new %s(\"EntityActorListenerFactory must be set before EntityManagerFactory\");\n",
297                 types.addType(RuntimeException.class));
298         out.format("        Context.listenerFactory = listenerFactory;\n");
299         out.format("    }\n\n");
300 
301         out.format("    public static %s createEntityManager() {\n",
302                 types.addType(EntityManager.class));
303         out.format("        return signaller.getEntityManagerFactory().createEntityManager();\n");
304         out.format("    }\n\n");
305         out.format("    public synchronized static void close() {\n");
306         out.format("        if (signaller != null) {\n");
307         out.format("            signaller.close();\n");
308         out.format("            signaller = null;\n");
309         out.format("        }\n");
310         out.format("    }\n\n");
311 
312         out.format("    public static <T extends %s<T>> T remove(T entity) {\n",
313                 types.addType(Entity.class));
314         out.format("        boolean emOpenAlready = em()!=null;\n");
315         out.format("        %s em;\n", types.addType(EntityManager.class));
316         out.format("        if (emOpenAlready)\n");
317         out.format("            em = em();\n");
318         out.format("        else\n");
319         out.format("            em = createEntityManager();\n");
320         out.format("        em.remove(entity);\n");
321         out.format("        if (!emOpenAlready)\n");
322         out.format("            em.close();\n");
323         out.format("        return entity;\n");
324         out.format("    }\n\n");
325 
326         out.format("    public static <T extends %s<T>> T load(T entity) {\n",
327                 types.addType(Entity.class));
328         out.format("        boolean emOpenAlready = em()!=null;\n");
329         out.format("        %s em;\n", types.addType(EntityManager.class));
330         out.format("        if (emOpenAlready)\n");
331         out.format("            em = em();\n");
332         out.format("        else\n");
333         out.format("            em = createEntityManager();\n");
334         out.format("        T t = em.merge(entity);\n");
335         out.format("        em.refresh(t);\n");
336         out.format("        if (!emOpenAlready)\n");
337         out.format("            em.close();\n");
338         out.format("        return t;\n");
339         out.format("    }\n\n");
340 
341         out.format("    public static %s em() {\n", types.addType(EntityManager.class));
342         out.format("        return signaller.getInfo().getCurrentEntityManager();\n");
343         out.format("    }\n\n");
344 
345         out.format("}");
346         out.close();
347 
348         String s = "package " + domainPackageName + ";\n\n";
349         s += types.getImports(domainPackageName + ".Context") + "\n";
350         s += bytes.toString();
351 
352         String filename = domainPackageName.replace(".", "/") + "/Context.java";
353         try
354 
355         {
356             try (FileOutputStream fos = new FileOutputStream(new File(destination, filename))) {
357                 fos.write(s.getBytes());
358             }
359         } catch (
360 
361         IOException e)
362 
363         {
364             throw new RuntimeException(e);
365         }
366 
367     }
368 
369     private static void log(String message) {
370         java.lang.System.out.println(message);
371     }
372 
373     private void createEntityJavaSource(Class cls, File destination, Lookups lookups) {
374         ClassWriterassWriter.html#ClassWriter">ClassWriter w = new ClassWriter(createClassInfo(cls));
375         String java = w.generate();
376         File file = new File(destination, getClassFilename(cls));
377         writeToFile(java.getBytes(), file);
378     }
379 
380     private ClassInfo createClassInfo(Class cls) {
381         Lookupsups.html#Lookups">Lookups lookups = new Lookups(domains, domain);
382         return new ClassInfo(nameManager, cls, domainPackageName, domainSchema, lookups);
383     }
384 
385     private String getClassJavaSimpleName(Class cls) {
386         return cls.getName().replace(" ", "").replace("-", "");
387     }
388 
389     private String getFullClassName(Class cls) {
390         return domainPackageName + "." + getClassJavaSimpleName(cls);
391     }
392 
393     private String getClassFilename(Class cls) {
394         String s = getFullClassName(cls);
395         return s.replace(".", "/") + ".java";
396     }
397 
398     // ----------------------------------------
399     // Static Utility Methods
400     // -----------------------------------------
401 
402     private static void writeToFile(byte[] bytes, File file) {
403         try {
404             file.getParentFile().mkdirs();
405             log("writing to " + file);
406             try (FileOutputStream fos = new FileOutputStream(file)) {
407                 fos.write(bytes);
408             }
409         } catch (IOException e) {
410             throw new RuntimeException(e);
411         }
412     }
413 
414     public static class Builder {
415         private String domainName;
416         private String domainPackageName;
417         private String domainSchema;
418         private Domains domains;
419         private File resourcesDirectory;
420         private boolean generatePersistenceXml;
421         private File entitySourceDirectory;
422         private final String implementationPackageName = "not used yet";
423         private File implementationSourceDirectory;
424         private final boolean overwriteImplementation = false;
425 
426         private Builder() {
427 
428         }
429 
430         public Builder domains(Domains domains) {
431             this.domains = domains;
432             return this;
433         }
434 
435         public Builder domainName(String domainName) {
436             this.domainName = domainName;
437             return this;
438         }
439 
440         public Builder domainSchema(String domainSchema) {
441             this.domainSchema = domainSchema;
442             return this;
443         }
444 
445         public Builder domainPackageName(String packageName) {
446             this.domainPackageName = packageName;
447             return this;
448         }
449 
450         public Builder generatedResourcesDirectory(File directory) {
451             this.resourcesDirectory = directory;
452             return this;
453         }
454 
455         public Builder generatedResourcesDirectory(String directory) {
456             this.resourcesDirectory = new File(directory);
457             return this;
458         }
459 
460         public Builder generatePersistenceXml(boolean generate) {
461             this.generatePersistenceXml = generate;
462             return this;
463         }
464 
465         public Builder generatedSourcesDirectory(File directory) {
466             this.entitySourceDirectory = directory;
467             return this;
468         }
469 
470         public Builder generatedSourcesDirectory(String directory) {
471             this.entitySourceDirectory = new File(directory);
472             return this;
473         }
474 
475         public CodeGeneratorJava build() {
476             if (implementationSourceDirectory == null)
477                 implementationSourceDirectory = entitySourceDirectory;
478             return new CodeGeneratorJava(domains, domainName, domainPackageName, domainSchema,
479                     entitySourceDirectory, resourcesDirectory, implementationPackageName,
480                     implementationSourceDirectory, generatePersistenceXml, overwriteImplementation);
481         }
482 
483     }
484 
485 }