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
39
40
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
98
99
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
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
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 }