1 package xuml.tools.model.compiler;
2
3 import java.io.ByteArrayOutputStream;
4 import java.io.PrintStream;
5 import java.io.Serializable;
6 import java.lang.reflect.InvocationTargetException;
7 import java.math.BigDecimal;
8 import java.math.BigInteger;
9 import java.util.Collection;
10 import java.util.Date;
11 import java.util.HashSet;
12 import java.util.List;
13 import java.util.Map;
14 import java.util.Set;
15 import java.util.concurrent.TimeUnit;
16 import java.util.regex.Pattern;
17
18 import javax.persistence.Basic;
19 import javax.persistence.CascadeType;
20 import javax.persistence.Column;
21 import javax.persistence.DiscriminatorColumn;
22 import javax.persistence.DiscriminatorType;
23 import javax.persistence.DiscriminatorValue;
24 import javax.persistence.Embeddable;
25 import javax.persistence.EmbeddedId;
26 import javax.persistence.Entity;
27 import javax.persistence.EntityManager;
28 import javax.persistence.FetchType;
29 import javax.persistence.GeneratedValue;
30 import javax.persistence.GenerationType;
31 import javax.persistence.Id;
32 import javax.persistence.Inheritance;
33 import javax.persistence.InheritanceType;
34 import javax.persistence.JoinColumn;
35 import javax.persistence.JoinColumns;
36 import javax.persistence.JoinTable;
37 import javax.persistence.Lob;
38 import javax.persistence.ManyToMany;
39 import javax.persistence.ManyToOne;
40 import javax.persistence.OneToMany;
41 import javax.persistence.OneToOne;
42 import javax.persistence.PrePersist;
43 import javax.persistence.PreUpdate;
44 import javax.persistence.Table;
45 import javax.persistence.Temporal;
46 import javax.persistence.TemporalType;
47 import javax.persistence.Transient;
48 import javax.persistence.UniqueConstraint;
49
50 import org.apache.commons.lang.StringEscapeUtils;
51
52 import com.google.common.base.MoreObjects;
53 import com.google.common.base.Objects;
54 import com.google.common.base.Optional;
55 import com.google.common.base.Preconditions;
56 import com.google.common.collect.Lists;
57 import com.google.common.collect.Maps;
58 import com.google.common.collect.Sets;
59
60 import scala.concurrent.duration.Duration;
61 import xuml.tools.model.compiler.ClassInfo.OtherId;
62 import xuml.tools.model.compiler.info.Mult;
63 import xuml.tools.model.compiler.info.MyAttributeExtensions;
64 import xuml.tools.model.compiler.info.MyEvent;
65 import xuml.tools.model.compiler.info.MyFind;
66 import xuml.tools.model.compiler.info.MyIdAttribute;
67 import xuml.tools.model.compiler.info.MyIndependentAttribute;
68 import xuml.tools.model.compiler.info.MyJoinColumn;
69 import xuml.tools.model.compiler.info.MyJoinTable;
70 import xuml.tools.model.compiler.info.MyParameter;
71 import xuml.tools.model.compiler.info.MyReferenceMember;
72 import xuml.tools.model.compiler.info.MySpecializations;
73 import xuml.tools.model.compiler.info.MySubclassRole;
74 import xuml.tools.model.compiler.info.MyTransition;
75 import xuml.tools.model.compiler.info.MyType;
76 import xuml.tools.model.compiler.info.MyTypeDefinition;
77 import xuml.tools.model.compiler.runtime.BehaviourFactoryNotSetException;
78 import xuml.tools.model.compiler.runtime.CreationEvent;
79 import xuml.tools.model.compiler.runtime.EntityHelper;
80 import xuml.tools.model.compiler.runtime.Event;
81 import xuml.tools.model.compiler.runtime.RelationshipNotEstablishedException;
82 import xuml.tools.model.compiler.runtime.Signaller;
83 import xuml.tools.model.compiler.runtime.TooManySpecializationsException;
84 import xuml.tools.model.compiler.runtime.ValidationException;
85 import xuml.tools.model.compiler.runtime.query.BooleanExpression;
86 import xuml.tools.model.compiler.runtime.query.Field;
87 import xuml.tools.model.compiler.runtime.query.NumericExpressionField;
88 import xuml.tools.model.compiler.runtime.query.SelectBuilder;
89 import xuml.tools.model.compiler.runtime.query.StringExpressionField;
90
91 public class ClassWriter {
92
93 private static final String BEHAVIOUR_COMMENT = "All actions like onEntry actions and defined\noperations are performed by this Behaviour class.";
94 private static final String STATE_COMMENT = "For internal use only by the state machine but is persisted by the jpa provider.";
95 private static final String MEMBER_MODIFIERS = "private";
96 private static final int MAX_VARCHAR_LENGTH = 65535;
97 public static boolean useJpaJoinedStrategyForSpecialization = false;
98 private final ClassInfo info;
99
100 public ClassWriter(ClassInfo info) {
101 this.info = info;
102 }
103
104 public String generate() {
105 Set<String> validationMethods = Sets.newTreeSet();
106 ByteArrayOutputStream bytes = new ByteArrayOutputStream();
107 PrintStream out = new PrintStream(bytes);
108 writeClassJavadoc(out, info);
109 writeClassAnnotation(out, info);
110 writeClassDeclaration(out, info);
111 writeConstructors(out, info);
112 writeEntityHelper(out, info);
113 writeIdMember(out, info, validationMethods);
114 writeUniqueIdMethod(out, info);
115 writeNonIdIndependentAttributeMembers(out, info, validationMethods);
116 writeStateMember(out, info);
117 writeReferenceMembers(out, info, validationMethods);
118 writeSuperclassValidationCheck(out, info, validationMethods);
119 writePreUpdateCheck(out, info, validationMethods);
120 writeIdGetterAndSetter(out, info);
121 writeNonIdIndependentAttributeGettersAndSetters(out, info);
122 writeStateGetterAndSetter(out, info);
123 writeStates(out, info);
124 writeEvents(out, info);
125 writeSignalMethods(out, info);
126 writeStaticCreateMethods(out, info);
127 writeMergeMethod(out, info);
128 writePersistMethod(out, info);
129 writeRemoveMethod(out, info);
130 writeRefreshMethod(out, info);
131 writeLoadMethod(out, info);
132 writeToStringMethod(out, info);
133 writeBehaviourInterface(out, info);
134 writeBehaviourFactoryInterface(out, info);
135 writeBehaviourFactoryCreator(out, info);
136 writeStaticFinderMethods(out, info);
137
138 writeQueryMethods(out, info);
139
140 writeClassClose(out);
141 ByteArrayOutputStream headerBytes = new ByteArrayOutputStream();
142 PrintStream header = new PrintStream(headerBytes);
143 writePackage(header, info);
144 writeImports(header, info);
145 out.close();
146 header.close();
147 return headerBytes.toString() + bytes.toString();
148 }
149
150 private void writeSuperclassValidationCheck(PrintStream out, ClassInfo info,
151 Set<String> validationMethods) {
152 if (info.isSuperclass()) {
153 List<MySpecializations> list = info.getSpecializations();
154 for (MySpecializations sp : list) {
155 String methodName = writeSpecializationValidationMethod(out, info, sp);
156 validationMethods.add(methodName);
157 }
158 }
159 }
160
161 private String writeSpecializationValidationMethod(PrintStream out, ClassInfo info,
162 MySpecializations sp) {
163 String methodName = "validateSpecializationR" + sp.getRnum();
164 out.format(" private void %s() {\n", methodName);
165 out.format(" int count = 0;\n");
166 for (String fieldName : sp.getFieldNames()) {
167 out.format(" if (%s != null)\n", fieldName);
168 out.format(" count++;\n");
169 }
170 out.format(" if (count == 0)\n");
171 out.format(" throw new %s(\"wrong number of specializations = \" + count);\n",
172 info.addType(RelationshipNotEstablishedException.class));
173 out.format(" if (count != 1)\n");
174 out.format(" throw new %s(\"wrong number of specializations = \" + count);\n",
175 info.addType(TooManySpecializationsException.class));
176 out.format(" }\n\n");
177 return methodName;
178 }
179
180 private void writeClassJavadoc(PrintStream out, ClassInfo info) {
181 jd(out, info.getClassDescription(), "");
182 }
183
184 private void writeClassAnnotation(PrintStream out, ClassInfo info) {
185 out.format("@%s\n", info.addType(Entity.class));
186 List<List<String>> uniqueConstraints = info.getUniqueConstraintColumnNames();
187 if (uniqueConstraints.size() >= 1) {
188 out.format("@%s(schema=\"%s\", name=\"%s\",\n", info.addType(Table.class),
189 info.getSchema(), info.getTable());
190 StringBuilder s = new StringBuilder();
191 for (List<String> list : uniqueConstraints) {
192 if (s.length() > 0)
193 s.append(",\n");
194 s.append(" @" + info.addType(UniqueConstraint.class) + "(columnNames={"
195 + getCommaDelimitedQuoted(list) + "})");
196 }
197 out.format(" uniqueConstraints={\n");
198 out.format("%s})\n", s);
199 } else {
200 out.format("@%s(schema=\"%s\", name=\"%s\")\n", info.addType(Table.class),
201 info.getSchema(), info.getTable());
202 }
203
204 if (useJpaJoinedStrategyForSpecialization)
205 writeJpaInheritanceAnnotations(out, info);
206 }
207
208 private void writeJpaInheritanceAnnotations(PrintStream out, ClassInfo info) {
209
210 if (info.isSuperclass()) {
211 out.format("@%s(strategy = %s.JOINED)\n", info.addType(Inheritance.class),
212 info.addType(InheritanceType.class));
213 out.format(
214 "//DiscriminatorColumn annotation is ignored by Hibernate but may be used\n");
215 out.format(
216 "//by other JPA providers. See https://hibernate.onjira.com/browse/ANN-140\n");
217 out.format(
218 "@%s(name = \"DISCRIMINATOR\", discriminatorType = %s.STRING, length = 255)\n",
219 info.addType(DiscriminatorColumn.class), info.addType(DiscriminatorType.class));
220 }
221 if (info.isSubclass()) {
222 MySubclassRole subclass = info.getSubclassRole();
223 out.format("@%s(\"%s\")\n", info.addType(DiscriminatorValue.class),
224 subclass.getDiscriminatorValue());
225 }
226 }
227
228 private void writeClassDeclaration(PrintStream out, ClassInfo info) {
229 String extension;
230 if (useJpaJoinedStrategyForSpecialization && info.isSubclass()) {
231 MySubclassRole subclass = info.getSubclassRole();
232 extension = " extends " + info.addType(subclass.getSuperclassJavaFullClassName());
233 } else
234 extension = "";
235
236 out.format("public class %s%s implements %s<%1$s> {\n\n", info.getJavaClassSimpleName(),
237 extension, info.addType(xuml.tools.model.compiler.runtime.Entity.class));
238 }
239
240 private Type getIdType(ClassInfo info) {
241 Type idType;
242 if (hasEmbeddedId())
243 idType = new Type(info.getPackage() + "." + info.getJavaClassSimpleName() + "."
244 + info.getEmbeddedIdSimpleClassName());
245 else
246 idType = info.getPrimaryIdAttributeMembers().get(0).getType().getType();
247 return idType;
248 }
249
250 private void writeConstructors(PrintStream out, ClassInfo info) {
251
252 jd(out, "No argument constructor required by JPA.", " ");
253 out.format(" public %s(){\n", info.getJavaClassSimpleName());
254 out.format(" //JPA requires no-arg constructor\n");
255 if (info.hasBehaviour()) {
256 out.format(" if (_behaviourFactory == null) {\n");
257 out.format(
258 " throw new %s(\"%s does not have "
259 + "a BehaviourFactory set. Use %s.setBehaviourFactory in your App class (one-time setup).\");\n",
260 info.addType(BehaviourFactoryNotSetException.class),
261 info.getJavaClassSimpleName(), info.getJavaClassSimpleName());
262 out.format(" }\n");
263 out.format(" _behaviour = _behaviourFactory.create(this);\n");
264 }
265 out.format(" }\n\n");
266 if (info.hasBehaviour()) {
267 String factoryTypeName = "BehaviourFactory";
268 String behaviourTypeName = "Behaviour";
269
270 writeBehaviourFields(out, info, factoryTypeName, behaviourTypeName);
271
272 writeBehaviourFactoryGetterAndSetter(out, factoryTypeName);
273 }
274
275 String idClassName = getIdClassName(info);
276
277 writeConstructorUsingId(out, info, idClassName);
278
279 writeCreatorUsingId(out, info, idClassName);
280
281 writeCreatorUsingCreationEvent(out, info);
282
283 }
284
285 private String getIdClassName(ClassInfo info) {
286 String idClassName;
287 if (!hasEmbeddedId()) {
288 if (info.getPrimaryIdAttributeMembers().size() == 0) {
289 throw new RuntimeException(
290 "Class does not have identifier, inherited or otherwise: "
291 + info.getJavaClassSimpleName());
292 }
293
294 idClassName = info
295 .addType(info.getPrimaryIdAttributeMembers().get(0).getType().getType());
296 } else
297 idClassName = info.getEmbeddedIdSimpleClassName();
298 return idClassName;
299 }
300
301 private void writeConstructorUsingId(PrintStream out, ClassInfo info, String idClassName) {
302
303 jd(out, "Constructor using id.", " ");
304 out.format(" public %s(%s id) {\n", info.getJavaClassSimpleName(), idClassName);
305 out.format(" this.id = id;\n");
306 out.format(" }\n\n");
307 }
308
309 private void writeCreatorUsingId(PrintStream out, ClassInfo info, String idClassName) {
310
311 jd(out, "Static creator method using id.", " ");
312 out.format(" public static %s create(%s id) {\n", info.getJavaClassSimpleName(),
313 idClassName);
314 out.format(" return new %s(id);\n", info.getJavaClassSimpleName());
315 out.format(" }\n\n");
316 }
317
318 private void writeCreatorUsingCreationEvent(PrintStream out, ClassInfo info) {
319
320 jd(out, "Static creator method using CreationEvent.", " ");
321 out.format(" public static %s create(%s<%s> creationEvent) {\n",
322 info.getJavaClassSimpleName(), info.addType(CreationEvent.class),
323 info.getJavaClassSimpleName());
324 out.format(" return Context.create(%s.class, creationEvent);\n",
325 info.getJavaClassSimpleName());
326 out.format(" }\n\n");
327 }
328
329 private void writeBehaviourFields(PrintStream out, ClassInfo info, String factoryTypeName,
330 String behaviourTypeName) {
331 jd(out, "If behaviour is not explicitly specified then the\n"
332 + "behaviour factory is used to create behaviour.", " ");
333 out.format(" private static volatile %s _behaviourFactory;\n\n", factoryTypeName);
334
335 jd(out, BEHAVIOUR_COMMENT, " ");
336 out.format(" @%s\n", info.addType(Transient.class));
337 out.format(" private %s _behaviour;\n\n", behaviourTypeName);
338 }
339
340 private void writeConstructorUsingBehaviour(PrintStream out, ClassInfo info,
341 String behaviourTypeName) {
342 jd(out, "Constructor using Behaviour.", " ");
343 if (info.useGuiceInjection())
344 out.format(" @%s\n", info.addType("com.google.inject.Inject"));
345 out.format(" public %s(%s behaviour){\n", info.getJavaClassSimpleName(),
346 behaviourTypeName);
347 out.format(" %s.checkNotNull(_behaviourFactory,\n",
348 info.addType(Preconditions.class));
349 out.format(
350 " \"You need to call static method setBehaviourFactory before instantiating \" + %s.class.getName());\n",
351 info.getJavaClassSimpleName());
352 out.format(" this._behaviour = behaviour;\n");
353 out.format(" }\n\n");
354 }
355
356 private void writeBehaviourFactoryGetterAndSetter(PrintStream out, String factoryTypeName) {
357 jd(out, "Sets the BehaviourFactory for all instances of\n"
358 + "this class. It will only be used when Behaviour\n"
359 + "is not explicitly provided in the constructor.", " ");
360 out.format(" public static void setBehaviourFactory(%s factory){\n", factoryTypeName);
361 out.format(" _behaviourFactory = factory;\n");
362 out.format(" }\n\n");
363
364 jd(out, "Sets the BehaviourFactory for all instances of\n"
365 + "this class using the given Behaviour class as the base. It will only be used when Behaviour\n"
366 + "is not explicitly provided in the constructor.", " ");
367 out.format(" public static void setBehaviourFactory(%s<? extends Behaviour> cls){\n",
368 info.addType(Class.class));
369 out.format(" _behaviourFactory = createBehaviourFactory(cls);\n");
370 out.format(" }\n\n");
371
372 jd(out, "Returns the singleton BehaviourFactory for this.", " ");
373 out.format(" public static %s getBehaviourFactory(){\n", factoryTypeName);
374 out.format(" return _behaviourFactory;\n");
375 out.format(" }\n\n");
376 }
377
378 private boolean hasEmbeddedId() {
379 return info.hasCompositeId();
380 }
381
382 private void writeEntityHelper(PrintStream out, ClassInfo info) {
383 jd(out, "The signaller used by the current Context. It will\nget injected into the EntityHelper.",
384 " ");
385 out.format(" private static %s signaller;\n\n", info.addType(Signaller.class));
386
387 jd(out, "Sets the Signaller to be used by the EntityHelper.", " ");
388 out.format(" static void setSignaller_(%s sig) {\n", info.addType(Signaller.class));
389 out.format(" signaller = sig;\n");
390 out.format(" }\n\n");
391
392 jd(out, "Helper for this class.", " ");
393 out.format(" @%s\n", info.addType(Transient.class));
394 out.format(" private %s _helper;\n\n", info.addType(EntityHelper.class));
395
396 jd(out, "Returns the Helper for this instance.", " ");
397 out.format(" public synchronized %s helper() {\n", info.addType(EntityHelper.class));
398 out.format(" if (_helper==null)\n");
399 out.format(" _helper = new %s(signaller,this);\n",
400 info.addType(EntityHelper.class));
401 out.format(" return _helper;\n");
402 out.format(" }\n\n");
403 }
404
405 private void writeIdMember(PrintStream out, ClassInfo info, Set<String> validationMethods) {
406 if (!hasEmbeddedId()) {
407 writeSimpleIdMember(out, info);
408 } else {
409 writeEmbeddedIdMember(out, info);
410
411 writeEmbeddedIdDeclaration(out, info);
412
413 writeEmbeddedIdConstructor(out, info);
414
415 writeEmbeddedIdFields(out, info, validationMethods);
416
417 writeEmbeddedIdGettersAndSetters(out, info);
418
419 writeEmbeddedIdToString(out, info);
420
421 writeEmbeddedIdEquals(out, info);
422
423 writeEmbeddedIdHashCode(out, info);
424
425 writeEmbeddedIdBuilder(out, info);
426
427 out.format(" }\n\n");
428 }
429 }
430
431 private void writeSimpleIdMember(PrintStream out, ClassInfo info) {
432 jd(out, "Primary identifier", " ");
433 out.format(" @%s\n", info.addType(Id.class));
434 MyIdAttribute attribute = info.getPrimaryIdAttributeMembers().get(0);
435
436 writeIndependentAttributeMember(out, "id", attribute.getColumnName(), false, " ",
437 attribute.getType(), attribute.getExtensions());
438 }
439
440 private void writeEmbeddedIdEquals(PrintStream out, ClassInfo info) {
441 out.format(" @%s\n", info.addType(Override.class));
442 out.format(" public boolean equals(Object obj) {\n");
443 out.format(" if (obj==null)\n");
444 out.format(" return false;\n");
445 out.format(" if (getClass() != obj.getClass())\n");
446 out.format(" return false;\n");
447 out.format(" final %1$s other = (%1$s) obj;\n",
448 info.getEmbeddedIdSimpleClassName());
449 out.format(" return ");
450 boolean first = true;
451 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
452 if (!first) {
453 out.println();
454 out.format(" && ");
455 }
456 out.format("%s.equal(this.%2$s, other.%2$s)", info.addType(Objects.class),
457 member.getFieldName());
458 first = false;
459 }
460 out.format(";\n");
461
462 out.format(" }\n\n");
463 }
464
465 private void writeEmbeddedIdHashCode(PrintStream out, ClassInfo info) {
466 out.format(" @%s\n", info.addType(Override.class));
467 out.format(" public int hashCode() {\n");
468 out.format(" return %s.hashCode(\n", info.addType(Objects.class));
469 boolean first = true;
470 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
471 if (!first) {
472 out.format(",\n");
473 }
474 out.format(" ");
475 out.format("this.%s", member.getFieldName());
476 first = false;
477 }
478 out.format(");\n");
479 out.format(" }\n\n");
480 }
481
482 private void writeEmbeddedIdBuilder(PrintStream out, ClassInfo info) {
483 out.format(" public static Builder builder() {\n");
484 out.format(" return new Builder();\n");
485 out.format(" }\n\n");
486 out.format(" public %s(Builder builder) {\n", info.getEmbeddedIdSimpleClassName());
487 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
488 out.format(" this.%s = builder.%s;\n", member.getFieldName(),
489 member.getFieldName());
490 }
491 out.format(" }\n\n");
492 out.format(" public static class Builder {\n\n");
493
494 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
495 out.format(" private %s %s;\n", info.addType(member.getType().getType()),
496 member.getFieldName());
497 }
498 out.println();
499 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
500 out.format(" public Builder %s(%s %s) {\n", member.getFieldName(),
501 info.addType(member.getType().getType()), member.getFieldName());
502 out.format(" this.%s = %s;\n", member.getFieldName(),
503 member.getFieldName());
504 out.format(" return this;\n");
505 out.format(" }\n\n");
506 }
507 out.format(" public %s build() {\n", info.getEmbeddedIdSimpleClassName());
508 out.format(" return new %s(this);\n", info.getEmbeddedIdSimpleClassName());
509 out.format(" }\n\n");
510 out.format(" }\n\n");
511
512 }
513
514 private void writeEmbeddedIdDeclaration(PrintStream out, ClassInfo info) {
515 out.format(" @%s\n", info.addType(Embeddable.class));
516 out.format(" @%s(\"serial\")\n", info.addType(SuppressWarnings.class));
517 out.format(" public static class %s implements %s {\n\n",
518 info.getEmbeddedIdSimpleClassName(), info.addType(Serializable.class));
519 out.format(" public %s() {\n", info.getEmbeddedIdSimpleClassName());
520 out.format(" //JPA requires no-arg constructor\n");
521 out.format(" }\n\n");
522 }
523
524 private void writeEmbeddedIdToString(PrintStream out, ClassInfo info) {
525 out.format("%s@%s\n", " ", info.addType(Override.class));
526 out.format("%spublic %s toString(){\n", " ", info.addType(String.class));
527 out.format("%s%s _s = new %s();\n", " ", info.addType(StringBuffer.class),
528 info.addType(StringBuffer.class));
529 out.format(" _s.append(\"%s [\");\n", info.getEmbeddedIdSimpleClassName());
530 boolean first = true;
531 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
532 if (!first)
533 out.format("%s_s.append(\",\");\n", " ");
534 out.format("%s_s.append(\"%s=\");\n", " ", member.getFieldName());
535 out.format("%s_s.append(%s.toString());\n", " ", member.getFieldName());
536 first = false;
537 }
538 out.format(" _s.append(\"]\");\n");
539 out.format("%sreturn _s.toString();\n", " ");
540 out.format("%s}\n\n", " ");
541 }
542
543 private void writeEmbeddedIdGettersAndSetters(PrintStream out, ClassInfo info) {
544 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
545 jd(out, "Returns the value of the attribute '" + member.getAttributeName() + "'",
546 " ");
547 out.format("%spublic %s get%s(){\n", " ",
548 info.addType(member.getType().getType()),
549 Util.upperFirst(member.getFieldName()));
550 out.format("%sreturn %s;\n", " ", member.getFieldName());
551 out.format("%s}\n\n", " ");
552
553 jd(out, "Sets the value of attribute '" + member.getAttributeName() + "'", " ");
554 out.format("%spublic void set%s(%s %s){\n", " ",
555 Util.upperFirst(member.getFieldName()),
556 info.addType(member.getType().getType()), member.getFieldName());
557 out.format("%sthis.%s = %s;\n", " ", member.getFieldName(),
558 member.getFieldName());
559 out.format("%s}\n\n", " ");
560 }
561 }
562
563 private void writeEmbeddedIdMember(PrintStream out, ClassInfo info) {
564 jd(out, "Id field.", " ");
565 out.format(" @%s\n", info.addType(EmbeddedId.class));
566 out.format(" %s %s %s;\n\n", MEMBER_MODIFIERS, info.getEmbeddedIdSimpleClassName(),
567 info.getEmbeddedIdAttributeName());
568 }
569
570 private void writeEmbeddedIdFields(PrintStream out, ClassInfo info,
571 Set<String> validationMethods) {
572 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
573 jd(out, "Field for attribute '" + member.getAttributeName() + "'.", " ");
574 if (member.getReferenceClass() == null) {
575 writeFieldAnnotation(out, member.getColumnName(), false, " ",
576 member.getType(), true, true);
577 } else {
578 writeFieldAnnotation(out, member.getColumnName(), false, " ",
579 member.getType(), false, false);
580 }
581 writeField(out, info, member.getType(), member.getFieldName(), " ");
582
583 writeAttributeValidationMethod(out, member.getFieldName(), member.getType(), info,
584 validationMethods, true, member.getExtensions().isGenerated());
585 }
586 }
587
588 private void writeField(PrintStream out, ClassInfo info, MyTypeDefinition type,
589 String fieldName, String indent) {
590 String defaultValue = type.getDefaultValue();
591 if (defaultValue == null)
592 out.format("%s%s %s %s;\n\n", indent, MEMBER_MODIFIERS, info.addType(type.getType()),
593 fieldName);
594 else if (type.getType().getBase().equals(Date.class.getName()))
595 out.format("%s%s %s %s = new %s(%s);\n\n", indent, MEMBER_MODIFIERS,
596 info.addType(type.getType()), fieldName, info.addType(type.getType()),
597 defaultValue);
598 else if (type.getType().getBase().equals(byte.class.getName()))
599 out.format("%s%s %s %s = new byte[0];\n\n", indent, MEMBER_MODIFIERS,
600 info.addType(type.getType()), fieldName, info.addType(type.getType()),
601 defaultValue);
602 else
603 out.format("%s%s %s %s = new %s(\"%s\");\n\n", indent, MEMBER_MODIFIERS,
604 info.addType(type.getType()), fieldName, info.addType(type.getType()),
605 defaultValue);
606 }
607
608 private void writeEmbeddedIdConstructor(PrintStream out, ClassInfo info) {
609
610 jd(out, "Primary identifier constructor.", " ");
611 out.format(" public %s(", info.getEmbeddedIdSimpleClassName());
612 boolean first = true;
613 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
614 if (!first)
615 out.format(", ");
616 out.format("%s %s", info.addType(member.getType().getType()), member.getFieldName());
617 first = false;
618 }
619 out.format(") {\n");
620 first = true;
621 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
622 out.format(" this.%s = %s;\n", member.getFieldName(), member.getFieldName());
623 first = false;
624 }
625 out.format(" }\n\n");
626 }
627
628 private void writeUniqueIdMethod(PrintStream out, ClassInfo info) {
629 jd(out, "Returns a unique id for this instance as a String. \nUsed for synchronizing access to entities.",
630 " ");
631 out.format(" @%s\n", info.addType(Override.class));
632 out.format(" public String uniqueId(){\n");
633 out.format(" return %s.class.getName() + \":\" + getId();\n",
634 info.getJavaClassSimpleName());
635 out.format(" }\n\n");
636
637 }
638
639 private void writeNonIdIndependentAttributeMembers(PrintStream out, ClassInfo info,
640 Set<String> validationMethods) {
641 for (MyIndependentAttribute attribute : info.getNonPrimaryIdIndependentAttributeMembers()) {
642 writeIndependentAttributeMember(out, attribute, " ");
643 writeAttributeValidationMethod(out, attribute, info, validationMethods);
644 }
645 }
646
647 private void writeAttributeValidationMethod(PrintStream out, MyIndependentAttribute attribute,
648 ClassInfo info, Set<String> validationMethods) {
649 writeAttributeValidationMethod(out, attribute.getFieldName(), attribute.getType(), info,
650 validationMethods, false, attribute.getExtensions().isGenerated());
651 }
652
653 private void writeAttributeValidationMethod(PrintStream out, String fieldName,
654 MyTypeDefinition type, ClassInfo info, Set<String> validationMethods,
655 boolean inEmbeddedId, boolean generated) {
656
657 if (generated)
658 return;
659 String indent;
660 if (inEmbeddedId)
661 indent = " ";
662 else
663 indent = " ";
664 MyType myType = type.getMyType();
665 String attributeConstantIdentifier = Util.toJavaConstantIdentifier(fieldName);
666 if (myType.equals(MyType.REAL) || myType.equals(MyType.INTEGER)) {
667 out.format("%sprivate static final %s %s_UPPER_LIMIT=new BigDecimal(\"%s\");\n", indent,
668 info.addType(BigDecimal.class), attributeConstantIdentifier,
669 type.getUpperLimit());
670 out.format("%sprivate static final %s %s_LOWER_LIMIT=new BigDecimal(\"%s\");\n\n",
671 indent, info.addType(BigDecimal.class), attributeConstantIdentifier,
672 type.getLowerLimit());
673 }
674 String validationMethodName = "validate" + Util.upperFirst(fieldName);
675 if (inEmbeddedId)
676 validationMethods.add("id." + validationMethodName);
677 else
678 validationMethods.add(validationMethodName);
679 jd(out, "Validates " + fieldName + " against type constraints.", indent);
680 out.format("%sprivate void %s() {\n", indent, validationMethodName);
681 Class<? extends RuntimeException> ex = ValidationException.class;
682 if (myType.equals(MyType.REAL) || myType.equals(MyType.INTEGER)) {
683 out.format("%s if (%s_UPPER_LIMIT.doubleValue() < %s) \n", indent,
684 attributeConstantIdentifier, fieldName);
685 out.format("%s throw new %s(\"upper limit of %s failed\");\n", indent,
686 info.addType(ex), type.getUpperLimit().toString());
687 out.format("%s if (%s_LOWER_LIMIT.doubleValue() > %s)\n", indent,
688 attributeConstantIdentifier, fieldName);
689 out.format("%s throw new %s(\"lower limit of %s failed\");\n", indent,
690 info.addType(ex), type.getLowerLimit().toString());
691 } else if (myType.equals(MyType.STRING)) {
692 if (type.getMinLength().intValue() > 0) {
693 out.format("%s if (%s == null || %s.length() < %s)\n", indent, fieldName,
694 fieldName, type.getMinLength().toString());
695 out.format("%s throw new %s(\"min length constraint not met\");\n", indent,
696 info.addType(ex));
697 }
698
699 if (type.getPrefix() != null) {
700 out.format("%s if (%s == null || !%s.startsWith(\"%s\"))\n", indent, fieldName,
701 fieldName, StringEscapeUtils.escapeJava(type.getPrefix()));
702 out.format("%s throw new %s(\"prefix constraint not met\");\n", indent,
703 info.addType(ex));
704 }
705 if (type.getSuffix() != null) {
706 out.format("%s if (%s == null || !%s.endsWith(\"%s\"))\n", indent, fieldName,
707 fieldName, StringEscapeUtils.escapeJava(type.getSuffix()));
708 out.format("%s throw new %s(\"suffix constraint not met\");\n", indent,
709 info.addType(ex));
710 }
711 if (type.getValidationPattern() != null) {
712 out.format("%s if (%s == null || !%s.matches(\"%s\", %s))\n", indent, fieldName,
713 info.addType(Pattern.class),
714 StringEscapeUtils.escapeJava(type.getValidationPattern()), fieldName);
715 out.format("%s throw new %s(\"validation pattern constraint not met\");\n",
716 indent, info.addType(ex));
717 }
718 } else if (myType.equals(MyType.BYTES)) {
719 if (type.getMinLength().intValue() > 0) {
720 out.format("%s if (%s == null || %s.length < %s)\n", indent, fieldName,
721 fieldName, type.getMinLength().toString());
722 out.format("%s throw new %s(\"min length constraint not met\");\n", indent,
723 info.addType(ex));
724 }
725 if (type.getMaxLength().intValue() > 0) {
726 out.format("%s if (%s == null || %s.length > %s)\n", indent, fieldName,
727 fieldName, type.getMaxLength().toString());
728 out.format("%s throw new %s(\"max length constraint not met\");\n", indent,
729 info.addType(ex));
730 }
731 }
732
733 out.format(" }\n\n");
734 }
735
736 private void writeStateMember(PrintStream out, ClassInfo info) {
737 if (info.hasBehaviour()) {
738 info.addType(Column.class);
739 jd(out, STATE_COMMENT, " ");
740 out.format(" @%s(name=\"state\",nullable=false)\n", info.addType(Column.class));
741 out.format(" %s String state;\n\n", MEMBER_MODIFIERS);
742 }
743 }
744
745 private void writeReferenceMembers(PrintStream out, ClassInfo info,
746 Set<String> validationMethods) {
747 for (MyReferenceMember ref : info.getReferenceMembers()) {
748 writeReferenceJavadoc(out, info, ref);
749 if (isRelationship(ref, Mult.ONE, Mult.ONE)) {
750 writeReferenceMembersOneToOne(out, info, validationMethods, ref);
751 } else if (isRelationship(ref, Mult.ONE, Mult.ZERO_ONE)) {
752 writeReferenceMembersOneToZeroOne(out, info, ref);
753 } else if (isRelationship(ref, Mult.ZERO_ONE, Mult.ONE)) {
754 writeReferenceMembersZeroOneToOne(out, info, ref);
755 } else if (isRelationship(ref, Mult.ONE, Mult.MANY)) {
756 writeReferenceMembersOneToMany(out, info, ref);
757 } else if (isRelationship(ref, Mult.MANY, Mult.ONE)) {
758 writeReferenceMembersManyToOne(out, info, ref);
759 } else if (isRelationship(ref, Mult.ONE, Mult.ONE_MANY)) {
760 writeReferenceMembersOneToOneMany(out, info, validationMethods, ref);
761 } else if (isRelationship(ref, Mult.ONE_MANY, Mult.ONE)) {
762 writeReferenceMembersOneManyToOne(out, info, validationMethods, ref);
763 } else if (isRelationship(ref, Mult.ZERO_ONE, Mult.ZERO_ONE)) {
764 writeReferenceMembersZeroOneToZeroOne(out, info, ref);
765 } else if (isRelationship(ref, Mult.ZERO_ONE, Mult.MANY)) {
766 writeReferenceMembersZeroOneToMany(out, info, ref);
767 } else if (isRelationship(ref, Mult.MANY, Mult.ZERO_ONE)) {
768 writeReferenceMembersManyToZeroOne(out, info, ref);
769 } else if (isRelationship(ref, Mult.ZERO_ONE, Mult.ONE_MANY)) {
770 writeValidationNotEmpty(out, ref.getFieldName(), validationMethods);
771 writeReferenceMembersZeroOneToMany(out, info, ref);
772 } else if (isRelationship(ref, Mult.ONE_MANY, Mult.ZERO_ONE)) {
773 writeReferenceMembersManyToZeroOne(out, info, ref);
774 } else if (isRelationship(ref, Mult.MANY, Mult.MANY)) {
775 writeManyToMany(out, info, ref);
776 } else if (isRelationship(ref, Mult.ONE_MANY, Mult.ONE_MANY)) {
777 writeManyToMany(out, info, ref);
778 } else if (isRelationship(ref, Mult.ONE_MANY, Mult.MANY)) {
779 writeManyToManySecondarySide(out, info, ref);
780 } else if (isRelationship(ref, Mult.MANY, Mult.ONE_MANY)) {
781 writeManyToManyPrimarySide(out, info, ref);
782 }
783 }
784 }
785
786 private void writeReferenceJavadoc(PrintStream out, ClassInfo info, MyReferenceMember ref) {
787 String javadoc = getReferenceJavadoc(info, ref);
788 jd(out, javadoc, " ");
789 }
790
791 private String getReferenceJavadoc(ClassInfo info, MyReferenceMember ref) {
792 return ref.getThisMult() + " " + info.getJavaClassSimpleName() + " "
793 + ref.getThatVerbClause() + " " + ref.getThatMult() + " "
794 + ref.getSimpleClassName();
795 }
796
797 private void writeReferenceMembersOneToOneMany(PrintStream out, ClassInfo info,
798 Set<String> validationMethods, MyReferenceMember ref) {
799 writeValidationNotEmpty(out, ref.getFieldName(), validationMethods);
800 writeReferenceMembersOneToMany(out, info, ref);
801 }
802
803 private void writeReferenceMembersManyToZeroOne(PrintStream out, ClassInfo info,
804 MyReferenceMember ref) {
805 out.format(" @%s(targetEntity=%s.class, fetch=%s.LAZY)\n", info.addType(ManyToOne.class),
806 info.addType(ref.getFullClassName()), info.addType(FetchType.class));
807 writeJoinColumnsAnnotation(out, ref, true, !ref.isInPrimaryId(), !ref.isInPrimaryId());
808 writeField(out, ref);
809 }
810
811 private void writeReferenceMembersZeroOneToMany(PrintStream out, ClassInfo info,
812 MyReferenceMember ref) {
813 out.format(
814 " @%s(mappedBy=\"%s\", cascade=%s.ALL, fetch=%s.LAZY, targetEntity=%s.class)\n",
815 info.addType(OneToMany.class), ref.getMappedBy(), info.addType(CascadeType.class),
816 info.addType(FetchType.class), info.addType(ref.getFullClassName()));
817 writeMultipleField(out, ref);
818 }
819
820 private void writeReferenceMembersZeroOneToZeroOne(PrintStream out, ClassInfo info,
821 MyReferenceMember ref) {
822 if (info.getJavaClassSimpleName().compareTo(ref.getSimpleClassName()) < 0) {
823
824 out.format(" //primary side of relationship\n");
825 out.format(" @%s(mappedBy=\"%s\", fetch=%s.LAZY, targetEntity=%s.class)\n",
826 info.addType(OneToOne.class), ref.getMappedBy(), info.addType(FetchType.class),
827 info.addType(ref.getFullClassName()));
828 } else {
829
830 out.format(" //secondary side of relationship\n");
831 out.format(" @%s(targetEntity=%s.class, fetch=%s.LAZY)\n",
832 info.addType(OneToOne.class), info.addType(ref.getFullClassName()),
833 info.addType(FetchType.class));
834 writeJoinColumnsAnnotation(out, ref, true, !ref.isInPrimaryId(), !ref.isInPrimaryId());
835 }
836 writeField(out, ref);
837 }
838
839 private void writeReferenceMembersOneManyToOne(PrintStream out, ClassInfo info,
840 Set<String> validationMethods, MyReferenceMember ref) {
841 writeValidationNotNull(out, ref.getFieldName(), validationMethods);
842 out.format(" @%s(targetEntity=%s.class)\n", info.addType(ManyToOne.class),
843 info.addType(ref.getFullClassName()));
844 writeJoinColumnsAnnotation(out, ref, false, !ref.isInPrimaryId(), !ref.isInPrimaryId());
845 writeField(out, ref);
846 }
847
848 private void writeReferenceMembersManyToOne(PrintStream out, ClassInfo info,
849 MyReferenceMember ref) {
850 out.format(" @%s(targetEntity=%s.class, fetch=%s.LAZY)\n", info.addType(ManyToOne.class),
851 ref.getFullClassName(), info.addType(FetchType.class));
852 writeJoinColumnsAnnotation(out, ref, false, !ref.isInPrimaryId(), !ref.isInPrimaryId());
853 writeField(out, ref);
854 }
855
856 private void writeReferenceMembersOneToMany(PrintStream out, ClassInfo info,
857 MyReferenceMember ref) {
858
859
860
861
862 out.format(
863 " @%s(mappedBy=\"%s\", cascade={%3$s.MERGE,%3$s.REFRESH,%3$s.REMOVE}, fetch=%4$s.LAZY, targetEntity=%5$s.class)\n",
864 info.addType(OneToMany.class), ref.getMappedBy(), info.addType(CascadeType.class),
865 info.addType(FetchType.class), info.addType(ref.getFullClassName()));
866 writeMultipleField(out, ref);
867 }
868
869 private void writeReferenceMembersZeroOneToOne(PrintStream out, ClassInfo info,
870 MyReferenceMember ref) {
871 out.format(" @%s(targetEntity=%s.class, cascade=%s.ALL, fetch=%s.LAZY)\n",
872 info.addType(OneToOne.class), info.addType(ref.getFullClassName()),
873 info.addType(CascadeType.class), info.addType(FetchType.class));
874 writeJoinColumnsAnnotation(out, ref, false, !ref.isInPrimaryId(), !ref.isInPrimaryId());
875 writeField(out, ref);
876 }
877
878 private void writeReferenceMembersOneToZeroOne(PrintStream out, ClassInfo info,
879 MyReferenceMember ref) {
880 if (isUnary(ref, info)) {
881 out.format(" @%s(targetEntity=%s.class, cascade=%s.ALL, fetch=%s.LAZY)\n",
882 info.addType(OneToOne.class), info.addType(ref.getFullClassName()),
883 info.addType(CascadeType.class), info.addType(FetchType.class));
884
885 writeJoinColumnsAnnotation(out, ref, true, !ref.isInPrimaryId(), !ref.isInPrimaryId());
886 writeField(out, ref);
887 } else {
888 out.format(" @%s(mappedBy=\"%s\", fetch=%s.LAZY, targetEntity=%s.class)\n",
889 info.addType(OneToOne.class), ref.getMappedBy(), info.addType(FetchType.class),
890 info.addType(ref.getFullClassName()));
891 writeField(out, ref);
892 }
893 }
894
895 private void writeReferenceMembersOneToOne(PrintStream out, ClassInfo info,
896 Set<String> validationMethods, MyReferenceMember ref) {
897
898
899 if (info.getJavaClassSimpleName().compareTo(ref.getSimpleClassName()) < 0) {
900 writeValidationNotNull(out, ref.getFieldName(), validationMethods);
901 out.format(" @%s(mappedBy=\"%s\", fetch=%s.LAZY, targetEntity=%s.class)\n",
902 info.addType(OneToOne.class), ref.getMappedBy(), info.addType(FetchType.class),
903 info.addType(ref.getFullClassName()));
904 writeField(out, ref);
905 } else {
906 writeReferenceMembersZeroOneToOne(out, info, ref);
907 }
908 }
909
910 private boolean isUnary(MyReferenceMember ref, ClassInfo info) {
911 return ref.getFullClassName().equals(info.getClassFullName());
912 }
913
914 private void writeValidationNotEmpty(PrintStream out, String fieldName,
915 Set<String> validationMethods) {
916 validationMethods.add("_validate" + Util.upperFirst(fieldName));
917 out.format(" private void _validate%s() {\n", Util.upperFirst(fieldName));
918 out.format(" if (%s.isEmpty())\n", fieldName);
919 out.format(" throw new %s(\"%s not established and is mandatory\");\n",
920 info.addType(RelationshipNotEstablishedException.class), "?");
921 out.format(" }\n\n");
922
923 }
924
925 private void writeValidationNotNull(PrintStream out, String fieldName,
926 Set<String> validationMethods) {
927 validationMethods.add("_validate" + Util.upperFirst(fieldName));
928 out.format(" private void _validate%s() {\n", Util.upperFirst(fieldName));
929 out.format(" if (%s == null)\n", fieldName);
930 out.format(" throw new %s(\"%s not established and is mandatory\");\n",
931 info.addType(RelationshipNotEstablishedException.class), "?");
932 out.format(" }\n\n");
933 }
934
935 private void writeJoinColumnsAnnotation(PrintStream out, MyReferenceMember ref,
936 boolean nullable, boolean insertable, boolean updatable) {
937 HashSet<String> cols = new HashSet<String>();
938 for (MyJoinColumn col : ref.getJoinColumns()) {
939 cols.add(col.getThisColumnName());
940 }
941
942 boolean twice = false;
943 info.getSpecializations();
944 for (MyReferenceMember r : info.getReferenceMembers()) {
945 if (r.getJoinColumns() != null && !ref.getFieldName().equals(r.getFieldName())) {
946 for (MyJoinColumn col : r.getJoinColumns()) {
947 if (cols.contains(col.getThisColumnName())) {
948 twice = true;
949 }
950 }
951 }
952 }
953 out.format(" @%s(value={\n", info.addType(JoinColumns.class));
954 boolean first = true;
955 for (MyJoinColumn col : ref.getJoinColumns()) {
956 if (!first)
957 out.format(",\n");
958 first = false;
959 out.format(
960 " @%s(name=\"%s\", referencedColumnName=\"%s\", nullable=%s, insertable=%s, updatable=%s)",
961 info.addType(JoinColumn.class), col.getThisColumnName(),
962 col.getOtherColumnName(), nullable, insertable && !twice, updatable && !twice);
963 }
964 out.format("})\n");
965 }
966
967 private void writeIdGetterAndSetter(PrintStream out, ClassInfo info) {
968 jd(out, "Returns the identifier for this entity.", " ");
969 out.format(" public %s getId() {\n", info.addType(getIdType(info)));
970 out.format(" return id;\n");
971 out.format(" }\n\n");
972 out.format(" public void setId(%s id) {\n", info.addType(getIdType(info)));
973 out.format(" this.id = id;\n");
974 out.format(" }\n\n");
975 out.format(" public %s setId_(%s id) {\n", info.getJavaClassSimpleName(),
976 info.addType(getIdType(info)));
977 out.format(" this.id = id;\n");
978 out.format(" return this;\n");
979 out.format(" }\n\n");
980 }
981
982 private void writeNonIdIndependentAttributeGettersAndSetters(PrintStream out, ClassInfo info) {
983 for (MyIndependentAttribute attribute : info.getNonPrimaryIdIndependentAttributeMembers()) {
984 writeIndependentAttributeGetterAndSetter(out, attribute);
985 }
986 }
987
988 private void writeStateGetterAndSetter(PrintStream out, ClassInfo info) {
989 if (info.hasBehaviour()) {
990 jd(out, STATE_COMMENT, " ");
991 out.format(" public String getState(){\n");
992 out.format(" return state;\n");
993 out.format(" }\n\n");
994 jd(out, STATE_COMMENT, " ");
995 out.format(" public void setState(String state){\n");
996 out.format(" this.state = state;\n");
997 out.format(" }\n\n");
998 jd(out, "Sets the current state. This should only be used when creating an instance without using the state machine.",
999 " ");
1000 out.format(" public void setState(%s state){\n",
1001 info.addType(info.getClassFullName() + ".State"));
1002 out.format(" this.state = state.toString();\n");
1003 out.format(" }\n\n");
1004 }
1005 }
1006
1007 private void writeStates(PrintStream out, ClassInfo info) {
1008 if (info.hasBehaviour()) {
1009 jd(out, "The list of all states from the state machine for this entity.", " ");
1010 out.format(" public static enum State {\n");
1011 boolean first = true;
1012 out.format(" ");
1013 for (String state : info.getStateNames()) {
1014 if (!first)
1015 out.format(",");
1016 out.format(info.getStateAsJavaIdentifier(state));
1017 first = false;
1018 }
1019 out.format(";\n");
1020 out.format(" }\n\n");
1021 }
1022 }
1023
1024 private void writeEvents(PrintStream out, ClassInfo info) {
1025 List<MyEvent> events = info.getEvents();
1026 if (events.size() == 0)
1027 return;
1028
1029
1030 jd(out, "Event declarations.", " ");
1031 out.format(" public static class Events {\n");
1032
1033
1034 Map<String, MyEvent> stateEvent = Maps.newHashMap();
1035 for (MyEvent event : info.getEvents()) {
1036 if (event.getStateName() != null)
1037 stateEvent.put(event.getStateName(), event);
1038 }
1039
1040 for (MyEvent event : stateEvent.values()) {
1041 jd(out, "Event signature for the state '" + event.getStateName() + "'", " ");
1042 out.format(" public static interface %s {\n\n",
1043 event.getStateSignatureInterfaceSimpleName());
1044
1045 for (MyParameter p : event.getParameters()) {
1046 out.format(" %s get%s();\n\n", info.addType(p.getType()),
1047 Util.upperFirst(p.getFieldName()));
1048
1049 }
1050 out.format(" }\n\n");
1051 }
1052
1053 for (MyEvent event : info.getEvents()) {
1054 String stateSignatureImplements;
1055 if (event.getStateName() != null)
1056 stateSignatureImplements = ", " + event.getStateSignatureInterfaceSimpleName();
1057 else
1058 stateSignatureImplements = "";
1059 String creationEventImplements;
1060 if (event.getCreates()) {
1061 creationEventImplements = ", " + info.addType(CreationEvent.class) + "<"
1062 + info.getJavaClassSimpleName() + ">";
1063 } else
1064 creationEventImplements = "";
1065 out.println();
1066 jd(out, "Event implementation for event '" + event.getName() + "'", " ");
1067
1068 out.format(" @%s(\"serial\")\n", info.addType(SuppressWarnings.class));
1069 out.format(" public static class %s implements %s<%s>, %s%s%s {\n\n",
1070 event.getSimpleClassName(), info.addType(Event.class),
1071 info.getJavaClassSimpleName(), info.addType(Serializable.class),
1072 stateSignatureImplements, creationEventImplements);
1073
1074
1075 StringBuffer signature = new StringBuffer();
1076 for (MyParameter p : event.getParameters()) {
1077 signature.append(p.getType());
1078 signature.append(";");
1079 }
1080
1081 out.format(" public static final String signatureKey = \"%s\";\n\n",
1082 signature.toString());
1083
1084 out.format(" public String signatureKey() {\n");
1085 out.format(" return signatureKey;\n");
1086 out.format(" }\n");
1087
1088 StringBuilder constructorBody = new StringBuilder();
1089 for (MyParameter p : event.getParameters()) {
1090 constructorBody.append(String.format(
1091 " if (%s == null) throw new %s(\"%s cannot be null\");\n",
1092 p.getFieldName(), info.addType(NullPointerException.class),
1093 p.getFieldName()));
1094 constructorBody.append(" this." + p.getFieldName() + " = "
1095 + p.getFieldName() + ";\n");
1096 }
1097
1098 StringBuilder constructor = new StringBuilder();
1099 constructor.append(" public " + event.getSimpleClassName() + "(");
1100 boolean first = true;
1101 for (MyParameter p : event.getParameters()) {
1102 out.format(" private final %s %s;\n", info.addType(p.getType()),
1103 p.getFieldName());
1104 if (!first)
1105 constructor.append(", ");
1106 constructor.append(info.addType(p.getType()) + " " + p.getFieldName());
1107 first = false;
1108 }
1109 constructor.append("){\n");
1110 constructor.append(constructorBody);
1111 constructor.append(" }\n");
1112 out.println();
1113 jd(out, "Constructor.", " ");
1114 out.println(constructor);
1115
1116
1117 for (MyParameter p : event.getParameters()) {
1118 out.format(" public %s get%s(){\n", info.addType(p.getType()),
1119 Util.upperFirst(p.getFieldName()));
1120 out.format(" return %s;\n", p.getFieldName());
1121 out.format(" }\n\n");
1122 }
1123
1124
1125 out.format(" private %s(Builder builder) {\n", event.getSimpleClassName());
1126 for (MyParameter p : event.getParameters()) {
1127 out.format(
1128 " if (builder.%s == null) throw new %s(\"%s cannot be null\");\n",
1129 p.getFieldName(), info.addType(NullPointerException.class),
1130 p.getFieldName());
1131 out.format(" this.%s = builder.%s;\n", p.getFieldName(),
1132 p.getFieldName());
1133 }
1134 out.format(" }\n\n");
1135
1136 out.format(" public static Builder builder() {\n");
1137 out.format(" return new Builder();\n");
1138 out.format(" }\n\n");
1139
1140
1141 out.format(" public static class Builder {\n");
1142 out.println();
1143 for (MyParameter p : event.getParameters()) {
1144 out.format(" private %s %s;\n", info.addType(p.getType()),
1145 p.getFieldName());
1146 }
1147 for (MyParameter p : event.getParameters()) {
1148 out.println();
1149 out.format(" public Builder %s(%s %s) {\n", p.getFieldName(),
1150 info.addType(p.getType()), p.getFieldName());
1151 out.format(" this.%s = %s;\n", p.getFieldName(),
1152 p.getFieldName());
1153 out.format(" return this;\n");
1154 out.format(" }\n");
1155 }
1156
1157 out.println();
1158 out.format(" public %s build() {\n", event.getSimpleClassName());
1159 out.format(" return new %s(this);\n", event.getSimpleClassName());
1160 out.format(" }\n");
1161
1162 out.format(" }\n");
1163
1164 if (event.getParameters().size() > 0) {
1165 out.println();
1166 out.format(" @%s\n", info.addType(Override.class));
1167 out.format(" public String toString() {\n");
1168 out.format(" return %s.toStringHelper(this.getClass())\n",
1169 info.addType(MoreObjects.class));
1170 for (MyParameter p : event.getParameters()) {
1171 out.format(" .add(\"%s\", %s)\n", p.getFieldName(),
1172 p.getFieldName());
1173 }
1174 out.format(" .toString();\n");
1175 out.format(" }\n");
1176 }
1177
1178
1179 out.format(" }\n\n");
1180
1181 }
1182 out.format(" }\n\n");
1183 }
1184
1185 private void writePreUpdateCheck(PrintStream out, ClassInfo info,
1186 Set<String> validationMethods) {
1187 jd(out, "Calls all validation methods just before updating database.", " ");
1188 out.format(" @%s\n", info.addType(PreUpdate.class));
1189 out.format(" void validateBeforeUpdate(){\n");
1190 for (String methodName : validationMethods)
1191 out.format(" %s();\n", methodName);
1192 out.format(" }\n\n");
1193
1194 jd(out, "Calls all validation methods just before first persist of this entity.", " ");
1195 out.format(" @%s\n", info.addType(PrePersist.class));
1196 out.format(" void validateBeforePersist(){\n");
1197 for (String methodName : validationMethods)
1198 out.format(" %s();\n", methodName);
1199 out.format(" }\n\n");
1200 }
1201
1202 private boolean isRelationship(MyReferenceMember ref, Multhref="../../../../xuml/tools/model/compiler/info/Mult.html#Mult">Mult here, Mult there) {
1203 return ref.getThisMult().equals(here) && ref.getThatMult().equals(there);
1204 }
1205
1206 private void writeManyToMany(PrintStream out, ClassInfo info, MyReferenceMember ref) {
1207 if (info.getJavaClassSimpleName().compareTo(ref.getSimpleClassName()) < 0) {
1208
1209 writeManyToManyPrimarySide(out, info, ref);
1210 } else {
1211
1212 writeManyToManySecondarySide(out, info, ref);
1213 }
1214 }
1215
1216 private void writeManyToManyPrimarySide(PrintStream out, ClassInfo info,
1217 MyReferenceMember ref) {
1218 out.format(" //primary side of relationship\n");
1219 out.format(" @%s(targetEntity=%s.class, cascade=%s.ALL, fetch=%s.LAZY)\n",
1220 info.addType(ManyToMany.class), info.addType(ref.getFullClassName()),
1221 info.addType(CascadeType.class), info.addType(FetchType.class));
1222
1223 writeJoinTableAnnotation(out, info, ref.getJoinTable());
1224
1225 writeMultipleField(out, ref);
1226 }
1227
1228 private void writeJoinTableAnnotation(PrintStream out, ClassInfo info, MyJoinTable jt) {
1229 out.format(" @%s(name=\"%s\", schema=\"%s\",\n", info.addType(JoinTable.class),
1230 jt.getJoinTable(), jt.getJoinTableSchema());
1231
1232 out.format(" joinColumns={\n");
1233 writeJoinColumns(out, info, jt.getJoinColumns());
1234 out.format("},\n");
1235 out.format(" inverseJoinColumns={\n");
1236 writeJoinColumns(out, info, jt.getInverseJoinColumns());
1237 out.format("})\n");
1238 }
1239
1240 private void writeJoinColumns(PrintStream out, ClassInfo info, List<MyJoinColumn> cols) {
1241 boolean first = true;
1242 for (MyJoinColumn jc : cols) {
1243 if (!first)
1244 out.format(",\n");
1245 out.format(" @%s(name=\"%s\", referencedColumnName=\"%s\")",
1246 info.addType(JoinColumn.class), jc.getThisColumnName(),
1247 jc.getOtherColumnName());
1248 first = false;
1249 }
1250 }
1251
1252 private void writeManyToManySecondarySide(PrintStream out, ClassInfo info,
1253 MyReferenceMember ref) {
1254 out.format(" //secondary side of relationship\n");
1255 out.format(
1256 " @%s(mappedBy=\"%s\", targetEntity=%s.class, cascade=%s.ALL, fetch=%s.LAZY)\n",
1257 info.addType(ManyToMany.class), ref.getMappedBy(),
1258 info.addType(ref.getFullClassName()), info.addType(CascadeType.class),
1259 info.addType(FetchType.class));
1260 writeMultipleField(out, ref);
1261 }
1262
1263 private void writeField(PrintStream out, MyReferenceMember ref) {
1264 out.format(" private %s %s;\n\n", info.addType(ref.getFullClassName()),
1265 ref.getFieldName());
1266 writeGetterAndSetter(out, info, ref.getSimpleClassName(), ref.getFullClassName(),
1267 ref.getFieldName(), false, getReferenceJavadoc(info, ref));
1268 writeRelateTo(out, ref);
1269 writeUnrelateTo(out, ref);
1270 }
1271
1272 private boolean isUnary(MyReferenceMember ref) {
1273 return ref.getFullClassName().equals(info.getClassFullName());
1274 }
1275
1276 private void writeRelateTo(PrintStream out, MyReferenceMember ref) {
1277
1278
1279 if (isUnary(ref))
1280 return;
1281
1282 String fieldName = ref.getFieldName();
1283 String mappedBy = Util.lowerFirst(ref.getMappedBy());
1284 writeReferenceJavadoc(out, info, ref);
1285 out.format(" public %s relateAcrossR%s(%s %s) {\n", info.getJavaClassSimpleName(),
1286 ref.getRnum(), info.addType(ref.getFullClassName()), fieldName);
1287 Mult thisMult = ref.getThisMult();
1288 Mult thatMult = ref.getThatMult();
1289
1290
1291 if (thatMult.equals(Mult.ONE) || thatMult.equals(Mult.ZERO_ONE)) {
1292 out.format(" set%s(%s);\n", Util.upperFirst(fieldName), fieldName);
1293 } else {
1294
1295
1296
1297
1298 }
1299
1300 if (thisMult.equals(Mult.ONE) || thisMult.equals(Mult.ZERO_ONE)) {
1301 out.format(" %s.set%s(this);\n", fieldName, Util.upperFirst(mappedBy));
1302 } else {
1303
1304
1305
1306
1307
1308 }
1309 out.format(" return this;\n");
1310 out.format(" }\n\n");
1311 }
1312
1313 private void writeUnrelateTo(PrintStream out, MyReferenceMember ref) {
1314
1315
1316 if (isUnary(ref))
1317 return;
1318
1319 String fieldName = ref.getFieldName();
1320 String mappedBy = Util.lowerFirst(ref.getMappedBy());
1321 writeReferenceJavadoc(out, info, ref);
1322 out.format(" public %s unrelateAcrossR%s(%s %s) {\n", info.getJavaClassSimpleName(),
1323 ref.getRnum(), info.addType(ref.getFullClassName()), fieldName);
1324 Mult thisMult = ref.getThisMult();
1325 Mult thatMult = ref.getThatMult();
1326
1327
1328 if (thatMult.equals(Mult.ONE) || thatMult.equals(Mult.ZERO_ONE)) {
1329 out.format(" set%s(null);\n", Util.upperFirst(fieldName), fieldName);
1330 } else {
1331 out.format(" get%s().remove(%s);\n", Util.upperFirst(fieldName), fieldName);
1332 }
1333
1334 if (thisMult.equals(Mult.ONE) || thisMult.equals(Mult.ZERO_ONE)) {
1335 out.format(" %s.set%s(null);\n", fieldName, Util.upperFirst(mappedBy));
1336 } else {
1337 out.format(" %s.get%s().remove(this);\n", fieldName, Util.upperFirst(mappedBy),
1338 fieldName);
1339 }
1340
1341 out.format(" return this;\n");
1342 out.format(" }\n\n");
1343 }
1344
1345 private void writeGetterAndSetter(PrintStream out, ClassInfo info, String simpleClassName,
1346 String fullClassName, String fieldName, boolean isMultiple, String javadoc) {
1347 String type;
1348 if (isMultiple)
1349 type = info.addType(new Type(Set.Typeeyword">class.getName(), new Type(fullClassName)));
1350 else
1351 type = info.addType(fullClassName);
1352
1353 jd(out, "Getter. " + javadoc, " ");
1354 out.format(" public %s get%s(){\n", type, Util.upperFirst(fieldName));
1355 out.format(" return %s;\n", fieldName);
1356 out.format(" }\n\n");
1357 jd(out, "Setter. " + javadoc, " ");
1358 out.format(" public void set%s(%s %s){\n", Util.upperFirst(fieldName), type, fieldName);
1359 out.format(" this.%1$s=%1$s;\n", fieldName);
1360 out.format(" }\n\n");
1361 }
1362
1363 private void writeMultipleField(PrintStream out, MyReferenceMember ref) {
1364 out.format(" private %s %s = %s.newHashSet();\n\n",
1365 info.addType(new Type(Set.Typeeyword">class.getName(), new Type(ref.getFullClassName()))),
1366 ref.getFieldName(), info.addType(Sets.class));
1367 writeGetterAndSetter(out, info, ref.getSimpleClassName(), ref.getFullClassName(),
1368 ref.getFieldName(), true, getReferenceJavadoc(info, ref));
1369 writeRelateTo(out, ref);
1370 writeUnrelateTo(out, ref);
1371 }
1372
1373 private void writeSignalMethods(PrintStream out, ClassInfo info) {
1374
1375
1376 jd(out, "Asychronously queues the given signal against this entity for processing.",
1377 " ");
1378 out.format(" @%s\n", info.addType(Override.class));
1379 out.format(" public %s signal(%s<%s> event) {\n", info.getJavaClassSimpleName(),
1380 info.addType(Event.class), info.getJavaClassSimpleName());
1381 if (info.hasBehaviour())
1382 out.format(" helper().signal(event);\n");
1383 else
1384 out.format(" //no behaviour for this class\n");
1385 out.format(" return this;\n");
1386 out.format(" }\n\n");
1387
1388 jd(out, "Asychronously queues the given signal against this entity for processing\nafter the delay specified. Delay cannot be null.",
1389 " ");
1390 out.format(" @%s\n", info.addType(Override.class));
1391 out.format(" public %s signal(%s<%s> event, %s delay) {\n",
1392 info.getJavaClassSimpleName(), info.addType(Event.class),
1393 info.getJavaClassSimpleName(), info.addType(Duration.class));
1394 if (info.hasBehaviour())
1395
1396
1397 out.format(" helper().signal(event, %s.of(delay));\n",
1398 info.addType(Optional.class));
1399 else
1400 out.format(" //no behaviour for this class\n");
1401 out.format(" return this;\n");
1402 out.format(" }\n\n");
1403
1404 jd(out, "Asychronously queues the given signal against this entity for processing\nat the epoch time in ms specified. Delay cannot be null.",
1405 " ");
1406 out.format(" @%s\n", info.addType(Override.class));
1407 out.format(" public %s signal(%s<%s> event, long time) {\n",
1408 info.getJavaClassSimpleName(), info.addType(Event.class),
1409 info.getJavaClassSimpleName());
1410 out.format(
1411 " return signal(event, %s.create(time-%s.currentTimeMillis(),%s.MILLISECONDS));\n",
1412 info.addType(Duration.class), info.addType(System.class),
1413 info.addType(TimeUnit.class));
1414 out.format(" }\n\n");
1415
1416 out.format(" public %s cancelSignal(String eventSignatureKey) {\n ",
1417 info.getJavaClassSimpleName());
1418
1419 out.format(" return this;\n");
1420 out.format(" }\n\n");
1421
1422 out.format(" public %s cancelSignal(Event<%s> event) {\n ",
1423 info.getJavaClassSimpleName(), info.getJavaClassSimpleName());
1424
1425 out.format(" return cancelSignal(event.signatureKey());\n");
1426 out.format(" }\n\n");
1427
1428 jd(out, "Synchronously runs the on entry procedure associated\n"
1429 + "with this event and also any signals to self that are\n"
1430 + "made during the procedure. This method should not\n"
1431 + "be called directly except in a unit testing scenario\n"
1432 + "perhaps. Call signal method instead.", " ");
1433 out.format(" @%s\n", info.addType(Override.class));
1434 out.format(" public %s event(%s<%s> event){\n\n", info.getJavaClassSimpleName(),
1435 info.addType(Event.class), info.getJavaClassSimpleName());
1436 if (info.hasBehaviour()) {
1437 out.format(" helper().beforeEvent();\n\n");
1438 out.format(" // process the event\n");
1439 boolean first = true;
1440 for (MyEvent event : info.getEvents()) {
1441 out.format(" ");
1442 if (!first)
1443 out.format("else ");
1444 out.format("if (event instanceof Events.%s){\n", event.getSimpleClassName());
1445 out.format(" processEvent((Events.%s) event);\n",
1446 event.getSimpleClassName());
1447 out.format(" }\n");
1448 first = false;
1449 }
1450 out.println();
1451 out.format(" helper().afterEvent();\n");
1452 }
1453 out.format(" return this;\n");
1454 out.format(" }\n\n");
1455 if (info.hasBehaviour()) {
1456 for (MyEvent event : info.getEvents()) {
1457
1458 jd(out, "Synchronously perform the change.", " ");
1459
1460 out.format(" private void processEvent(Events.%s event){\n",
1461 event.getSimpleClassName());
1462 boolean first = true;
1463 for (MyTransition transition : info.getTransitions()) {
1464
1465 if (transition.getEventName().equals(event.getName())) {
1466 if (first)
1467 out.format(" if");
1468 else
1469 out.format(" else if");
1470 first = false;
1471 if (transition.getFromState() == null)
1472
1473 out.format(" (state==null){\n");
1474 else
1475 out.format(" (state.equals(State.%s.toString())){\n",
1476 info.getStateAsJavaIdentifier(transition.getFromState()));
1477 out.format(" state=State.%s.toString();\n",
1478 info.getStateAsJavaIdentifier(transition.getToState()));
1479 out.format(" synchronized(this) {\n");
1480 out.format(" _behaviour.onEntry%s(event);\n",
1481 Util.upperFirst(Util.toJavaIdentifier(transition.getToState())));
1482 out.format(" }\n");
1483 out.format(" }\n");
1484 }
1485 }
1486 out.format(" }\n\n");
1487 }
1488 }
1489 }
1490
1491 private void writeStaticCreateMethods(PrintStream out, ClassInfo info) {
1492 if (info.hasBehaviour()) {
1493 for (MyTransition t : info.getTransitions()) {
1494 if (t.isCreationTransition()) {
1495 jd(out, "Static creator method associated with the creation transition to '"
1496 + t.getToState() + "' via event '" + t.getEventName() + "'.", " ");
1497 out.format(" public static %s create(%s em, %s<%s> event) {\n",
1498 info.getJavaClassSimpleName(), info.addType(EntityManager.class),
1499 info.addType(CreationEvent.class), info.getJavaClassSimpleName());
1500 out.format(" %s entity = new %s();\n", info.getJavaClassSimpleName(),
1501 info.getJavaClassSimpleName());
1502 out.format(" entity.event(event);\n");
1503 out.format(" em.persist(entity);\n");
1504 out.format(" return entity;\n");
1505 out.format(" }\n\n");
1506 }
1507 }
1508 }
1509 }
1510
1511 private void writeMergeMethod(PrintStream out, ClassInfo info) {
1512 jd(out, "Same as EntityManager.merge() except allows method chaining.\n"
1513 + "Returns a new merged instance.", " ");
1514 out.format(" public %s merge(%s em) {\n", info.getJavaClassSimpleName(),
1515 info.addType(EntityManager.class));
1516 out.format(" return em.merge(this);\n");
1517 out.format(" }\n\n");
1518 }
1519
1520 private void writePersistMethod(PrintStream out, ClassInfo info) {
1521 jd(out, "Same as EntityManager.persist() except allows method chaining. Returns this.",
1522 " ");
1523 out.format(" public %s persist(%s em) {\n", info.getJavaClassSimpleName(),
1524 info.addType(EntityManager.class));
1525 out.format(" em.persist(this);\n");
1526 out.format(" return this;\n");
1527 out.format(" }\n\n");
1528 out.println();
1529 jd(out, "Same as {@code persist(Context.em())}. Returns this.", " ");
1530 out.format(" public %s persist() {\n", info.getJavaClassSimpleName(),
1531 info.addType(EntityManager.class));
1532 out.format(" Context.em().persist(this);\n");
1533 out.format(" return this;\n");
1534 out.format(" }\n\n");
1535 }
1536
1537 private void writeRefreshMethod(PrintStream out, ClassInfo info) {
1538 jd(out, "Same as EntityManager.refresh() except inverted to facilitate method chaining. Returns this.",
1539 " ");
1540 out.format(" public %s refresh(%s em) {\n", info.getJavaClassSimpleName(),
1541 info.addType(EntityManager.class));
1542 out.format(" em.refresh(this);\n");
1543 out.format(" return this;\n");
1544 out.format(" }\n\n");
1545 }
1546
1547 private void writeRemoveMethod(PrintStream out, ClassInfo info) {
1548 jd(out, "Same as EntityManager.remove() except inverted to facilitate method chaining. Returns this.",
1549 " ");
1550 out.format(" public %s remove(%s em) {\n", info.getJavaClassSimpleName(),
1551 info.addType(EntityManager.class));
1552 out.format(" em.remove(this);\n");
1553 out.format(" return this;\n");
1554 out.format(" }\n\n");
1555
1556 jd(out, "Same as EntityManager.remove() except inverted to facilitate method chaining. Returns this.",
1557 " ");
1558 out.format(" public %s remove() {\n", info.getJavaClassSimpleName(),
1559 info.addType(EntityManager.class));
1560 out.format(" Context.remove(this);\n");
1561 out.format(" return this;\n");
1562 out.format(" }\n\n");
1563
1564 jd(out, "Same as this.remove()", " ");
1565 out.format(" public %s delete() {\n", info.getJavaClassSimpleName(),
1566 info.addType(EntityManager.class));
1567 out.format(" return remove();\n");
1568 out.format(" }\n\n");
1569 }
1570
1571 private void writeLoadMethod(PrintStream out, ClassInfo info) {
1572 jd(out, "Does a merge then a refresh and returns a new updated merged instance.", " ");
1573 out.format(" public %s load(%s em) {\n", info.getJavaClassSimpleName(),
1574 info.addType(EntityManager.class));
1575 out.format(" return merge(em).refresh(em);\n");
1576 out.format(" }\n\n");
1577
1578 out.format(" public %s load() {\n", info.getJavaClassSimpleName());
1579 out.format(" return Context.load(this);\n");
1580 out.format(" }\n\n");
1581
1582 }
1583
1584 private void writeToStringMethod(PrintStream out, ClassInfo info) {
1585
1586 }
1587
1588 private void writeBehaviourInterface(PrintStream out, ClassInfo info) {
1589
1590 if (info.getEvents().size() == 0)
1591 return;
1592 jd(out, "On entry procedures for this entity.", " ");
1593 out.format(" public static interface Behaviour {\n\n");
1594
1595
1596 Map<String, MyEvent> stateEvent = Maps.newLinkedHashMap();
1597 for (MyEvent event : info.getEvents()) {
1598 if (event.getStateName() != null)
1599 stateEvent.put(event.getStateName(), event);
1600 }
1601 List<MyEvent> nonStateEvents = Lists.newArrayList();
1602 for (MyEvent event : info.getEvents()) {
1603 if (event.getStateName() == null)
1604 nonStateEvents.add(event);
1605 }
1606
1607
1608
1609
1610 Set<String> methods = new HashSet<String>();
1611
1612 for (MyEvent event : stateEvent.values()) {
1613 String methodSuffix = Util.upperFirst(Util.toJavaIdentifier(event.getStateName()));
1614 methods.add(String.format(" void onEntry%s(Events.%s event);\n\n", methodSuffix,
1615 event.getStateSignatureInterfaceSimpleName()));
1616 }
1617
1618 for (MyEvent event : nonStateEvents) {
1619 for (MyTransition transition : info.getTransitions()) {
1620
1621 String methodSuffix = Util
1622 .upperFirst(Util.toJavaIdentifier(transition.getToState()));
1623 if (transition.getEventName().equals(event.getName())) {
1624 methods.add(String.format(" void onEntry%s(Events.%s event);\n\n",
1625 methodSuffix, event.getSimpleClassName()));
1626 }
1627 }
1628 }
1629
1630 for (String method : methods)
1631 out.format(method);
1632
1633 out.format(" }\n\n");
1634 }
1635
1636 private void writeBehaviourFactoryInterface(PrintStream out, ClassInfo info) {
1637 if (info.getEvents().size() == 0)
1638 return;
1639 jd(out, "A factory that creates behaviour for a given entity.", " ");
1640 out.format(" public static interface BehaviourFactory {\n\n");
1641 out.format(" Behaviour create(%s entity);\n\n", info.getJavaClassSimpleName());
1642 out.format(" }\n\n");
1643 }
1644
1645 private void writeBehaviourFactoryCreator(PrintStream out, ClassInfo info) {
1646 if (info.getEvents().size() == 0)
1647 return;
1648 jd(out, "Returns a BehaviourFactory on the assumption that the given class\nhas a single constructor with one parameter of type "
1649 + info.getJavaClassSimpleName() + ".", " ");
1650 out.format(
1651 " public static BehaviourFactory createBehaviourFactory(final Class<? extends Behaviour> cls) {\n");
1652 out.format(" return new BehaviourFactory() {\n");
1653 out.format(" @%s\n", info.addType(Override.class));
1654 out.format(" public Behaviour create(%s entity) {\n",
1655 info.getJavaClassSimpleName());
1656 out.format(" if (cls.getConstructors().length != 1)\n");
1657 out.format(" throw new RuntimeException(\n");
1658 out.format(
1659 " \"expected only one constructor in the Behaviour implementation\");\n");
1660 out.format(" try {\n");
1661
1662
1663 out.format(
1664 " return (Behaviour) cls.getConstructors()[0].newInstance(entity);\n");
1665 out.format(" } catch (%s e) {\n",
1666 info.addType(InstantiationException.class));
1667 out.format(" throw new RuntimeException(e);\n");
1668 out.format(" } catch (%s e) {\n",
1669 info.addType(IllegalAccessException.class));
1670 out.format(" throw new RuntimeException(e);\n");
1671 out.format(" } catch (%s e) {\n",
1672 info.addType(IllegalArgumentException.class));
1673 out.format(" throw new RuntimeException(e);\n");
1674 out.format(" } catch (%s e) {\n",
1675 info.addType(InvocationTargetException.class));
1676 out.format(" throw new RuntimeException(e);\n");
1677 out.format(" } catch (%s e) {\n", info.addType(SecurityException.class));
1678 out.format(" throw new RuntimeException(e);\n");
1679 out.format(" }\n");
1680 out.format(" }\n");
1681 out.format(" };\n");
1682 out.format(" }\n\n");
1683 }
1684
1685 private void writeIndependentAttributeMember(PrintStream out, MyIndependentAttribute attribute,
1686 String indent) {
1687 writeIndependentAttributeMember(out, attribute.getFieldName(), attribute.getColumnName(),
1688 attribute.isNullable(), indent, attribute.getType(), attribute.getExtensions());
1689 }
1690
1691 private void writeIndependentAttributeMember(PrintStream out, String fieldName,
1692 String columnName, boolean isNullable, String indent, MyTypeDefinition type,
1693 MyAttributeExtensions extensions) {
1694 if (extensions.getDocumentationContent() != null)
1695 jd(out, extensions.getDocumentationContent(), indent);
1696 writeGeneratedAnnotation(out, extensions.isGenerated(), indent);
1697 writeFieldAnnotation(out, columnName, isNullable, indent, type, true, true);
1698 writeField(out, info, type, fieldName, indent);
1699 }
1700
1701 private void writeGeneratedAnnotation(PrintStream out, boolean generated, String indent) {
1702 if (generated) {
1703 out.format("%s@%s(strategy=%s.AUTO)\n", indent, info.addType(GeneratedValue.class),
1704 info.addType(GenerationType.class));
1705 }
1706 }
1707
1708 private void writeFieldAnnotation(PrintStream out, String columnName, boolean isNullable,
1709 String indent, MyTypeDefinition type, boolean insertable, boolean updatable) {
1710 final String length;
1711 boolean isLong = type.getMyType().equals(MyType.STRING)
1712 && type.getMaxLength().compareTo(BigInteger.valueOf(MAX_VARCHAR_LENGTH)) > 0;
1713 if ((type.getMyType().equals(MyType.STRING) && isLong)
1714 || (type.getMyType().equals(MyType.BYTES))) {
1715 out.format("%s@%s\n", indent, info.addType(Lob.class));
1716 out.format("%s@%s(fetch=%s.LAZY)\n", indent, info.addType(Basic.class),
1717 info.addType(FetchType.class));
1718
1719 }
1720 if (type.getMyType().equals(MyType.STRING) && !isLong)
1721 length = ",length=" + type.getMaxLength();
1722 else
1723 length = "";
1724 final String insertableParameter;
1725 if (!insertable)
1726 insertableParameter = ",insertable=false";
1727 else
1728 insertableParameter = "";
1729 final String updatableParameter;
1730 if (!updatable)
1731 updatableParameter = ",updatable=false";
1732 else
1733 updatableParameter = "";
1734 final String precision;
1735 if (type.getMyType().equals(MyType.REAL))
1736 precision = ",precision=" + type.getPrecision();
1737 else
1738 precision = "";
1739
1740 out.format("%s@%s(name=\"%s\", nullable=%s%s%s%s%s)\n", indent, info.addType(Column.class),
1741 columnName, isNullable, length, insertableParameter, updatableParameter, precision);
1742 if (type.getMyType().equals(MyType.DATE))
1743 out.format("%s@%s(%s.DATE)\n", indent, info.addType(Temporal.class),
1744 info.addType(TemporalType.class));
1745 else if (type.getMyType().equals(MyType.TIMESTAMP))
1746 out.format("%s@%s(%s.TIMESTAMP)\n", indent, info.addType(Temporal.class),
1747 info.addType(TemporalType.class));
1748 }
1749
1750 private void writeIndependentAttributeGetterAndSetter(PrintStream out,
1751 MyIndependentAttribute attribute) {
1752 String type = info.addType(attribute.getType().getType());
1753 String doco = getDocumentation(attribute);
1754 jd(out, "Returns " + attribute.getFieldName() + ". " + doco, " ");
1755 if (attribute.getFieldName().equals("id")) {
1756 info.addType(Override.class);
1757 out.format(" @Override\n");
1758 }
1759 out.format(" public %s get%s(){\n", type, Util.upperFirst(attribute.getFieldName()));
1760 out.format(" return %s;\n", attribute.getFieldName());
1761 out.format(" }\n\n");
1762
1763 jd(out, "Sets " + attribute.getFieldName() + " to the given value. " + doco, " ");
1764 out.format(" public void set%s(%s %s){\n", Util.upperFirst(attribute.getFieldName()),
1765 type, attribute.getFieldName());
1766 out.format(" this.%1$s = %1$s;\n", attribute.getFieldName());
1767 out.format(" }\n\n");
1768
1769 jd(out, "Sets the attribute to the given value and returns this\n(enables method chaining).",
1770 " ");
1771 out.format(" public %s set%s_(%s %s){\n", info.getJavaClassSimpleName(),
1772 Util.upperFirst(attribute.getFieldName()), type, attribute.getFieldName());
1773 out.format(" set%s(%s);\n", Util.upperFirst(attribute.getFieldName()),
1774 attribute.getFieldName());
1775 out.format(" return this;\n");
1776 out.format(" }\n\n");
1777 }
1778
1779 private static String getDocumentation(MyIndependentAttribute attribute) {
1780 String doco = attribute.getExtensions().getDocumentationContent();
1781 if (doco == null)
1782 return "";
1783 else {
1784 doco = doco.trim();
1785 if (doco.endsWith(".") || doco.endsWith("?") || doco.endsWith("!"))
1786 return doco;
1787 else
1788 return doco.concat(".");
1789 }
1790 }
1791
1792 private void writeStaticFinderMethods(PrintStream out, ClassInfo info) {
1793
1794 out.format(" public static %s<%s> find(%s id) {\n", info.addType(Optional.class),
1795 info.getJavaClassSimpleName(), info.addType(getIdType(info).getBase()),
1796 getIdType(info).getClass().getSimpleName());
1797
1798 out.format(" if (Context.em()!=null) {\n");
1799 out.format(" return %s.fromNullable(Context.em().find(%s.class,id));\n",
1800 info.addType(Optional.class), info.getJavaClassSimpleName());
1801 out.format(" } else {\n");
1802 out.format(" %s em = Context.createEntityManager();\n",
1803 info.addType(EntityManager.class));
1804 out.format(" try {\n");
1805 out.format(" %s result = em.find(%s.class,id);\n",
1806 info.getJavaClassSimpleName(), info.getJavaClassSimpleName());
1807 out.format(" return %s.fromNullable(result);\n",
1808 info.addType(Optional.class));
1809 out.format(" } finally {\n");
1810 out.format(" em.close();\n");
1811 out.format(" }\n");
1812
1813 out.format(" }\n");
1814 out.format(" }\n\n");
1815
1816 for (MyFind find : info.getFinders()) {
1817 jd(out, "Static finder method generated due to xuml-tools extension <b>Find</b>.",
1818 " ");
1819 StringBuffer findBy = new StringBuffer();
1820 for (MyIndependentAttribute attribute : find.getAttributes()) {
1821 findBy.append(Util.upperFirst(attribute.getFieldName()));
1822 }
1823
1824 out.format(" public static %s<%s> findBy%s(", info.addType(List.class),
1825 info.getJavaClassSimpleName(), findBy.toString());
1826 {
1827 boolean first = true;
1828 for (MyIndependentAttribute attribute : find.getAttributes()) {
1829 if (!first)
1830 out.format(", ");
1831 out.format("%s %s", info.addType(attribute.getType().getType()),
1832 attribute.getFieldName());
1833 first = false;
1834 }
1835 }
1836 out.format(") {\n");
1837 out.format(" %s em = Context.createEntityManager();\n",
1838 info.addType(EntityManager.class));
1839 out.format(" @%s(\"unchecked\")\n", info.addType(SuppressWarnings.class));
1840 out.format(" %s<%s> list = em.createQuery(\"select e from %s e where ",
1841 info.addType(List.class), info.getJavaClassSimpleName(),
1842 info.getJavaClassSimpleName());
1843 {
1844 boolean first = true;
1845 for (MyIndependentAttribute attribute : find.getAttributes()) {
1846 if (!first)
1847 out.format(" and ");
1848 out.format("e.%s=:%s", attribute.getFieldName(), attribute.getFieldName());
1849 first = false;
1850 }
1851 }
1852 out.format("\")");
1853 for (MyIndependentAttribute attribute : find.getAttributes()) {
1854 out.format("\n .setParameter(\"%s\", %s)", attribute.getFieldName(),
1855 attribute.getFieldName());
1856 }
1857 out.format("\n .getResultList();\n");
1858 out.format("\n em.close();\n");
1859 out.format(" return list;\n");
1860 out.format(" }\n\n");
1861
1862 jd(out, "Static finder method generated due to xuml-tools extension <b>Find</b>.",
1863 " ");
1864
1865 out.format(" public static %s<%s> findBy%s(%s em, ", info.addType(List.class),
1866 info.getJavaClassSimpleName(), findBy.toString(),
1867 info.addType(EntityManager.class));
1868 {
1869 boolean first = true;
1870 for (MyIndependentAttribute attribute : find.getAttributes()) {
1871 if (!first)
1872 out.format(", ");
1873 out.format("%s %s", info.addType(attribute.getType().getType()),
1874 attribute.getFieldName());
1875 first = false;
1876 }
1877 }
1878 out.format(") {\n");
1879 out.format(" @%s(\"unchecked\")\n", info.addType(SuppressWarnings.class));
1880 out.format(" %s<%s> list = em.createQuery(\"select e from %s e where ",
1881 info.addType(List.class), info.getJavaClassSimpleName(),
1882 info.getJavaClassSimpleName());
1883 {
1884 boolean first = true;
1885 for (MyIndependentAttribute attribute : find.getAttributes()) {
1886 if (!first)
1887 out.format(" and ");
1888 out.format("e.%s=:%s", attribute.getFieldName(), attribute.getFieldName());
1889 first = false;
1890 }
1891 }
1892 out.format("\")");
1893 for (MyIndependentAttribute attribute : find.getAttributes()) {
1894 out.format("\n .setParameter(\"%s\", %s)", attribute.getFieldName(),
1895 attribute.getFieldName());
1896 }
1897 out.format("\n .getResultList();\n");
1898 out.format(" return list;\n");
1899 out.format(" }\n\n");
1900
1901 }
1902 }
1903
1904 private void writeClassClose(PrintStream out) {
1905 out.format("}");
1906 }
1907
1908 private void writePackage(PrintStream out, ClassInfo info) {
1909 out.format("package %s;\n\n", info.getPackage());
1910 }
1911
1912 private void writeImports(PrintStream out, ClassInfo info) {
1913 out.println(info.getImports(info.getClassFullName()));
1914 }
1915
1916
1917
1918
1919
1920 private void jd(PrintStream out, String comment, String indent) {
1921 out.format("%s/**\n", indent);
1922 for (String line : comment.split("\n")) {
1923 out.format("%s * %s\n", indent, line);
1924 }
1925 out.format("%s */\n", indent);
1926 }
1927
1928 private String getDelimited(Collection<String> items, String delimiter, String itemBefore,
1929 String itemAfter) {
1930 StringBuilder s = new StringBuilder();
1931 for (String item : items) {
1932 if (s.length() > 0)
1933 s.append(delimiter);
1934 s.append(itemBefore);
1935 s.append(item);
1936 s.append(itemAfter);
1937 }
1938 return s.toString();
1939 }
1940
1941 private String getCommaDelimitedQuoted(List<String> items) {
1942 return getDelimited(items, ",", "\"", "\"");
1943 }
1944
1945 private void writeQueryMethods(PrintStream out, ClassInfo info) {
1946
1947 out.format(" public static class Attribute {\n");
1948 for (MyIndependentAttribute member : info.getNonPrimaryIdIndependentAttributeMembers()) {
1949 MyType type = member.getType().getMyType();
1950 String fieldName = member.getFieldName();
1951 String fieldNameInQuery = fieldName;
1952 writeQueryField(out, info, type, fieldName, fieldNameInQuery);
1953 }
1954 for (MyIdAttribute member : info.getPrimaryIdAttributeMembers()) {
1955 MyType type = member.getType().getMyType();
1956 String fieldName = member.getFieldName();
1957 String fieldNameInQuery;
1958 if (hasEmbeddedId()) {
1959 fieldNameInQuery = "id." + fieldName;
1960 } else {
1961 fieldNameInQuery = fieldName;
1962 }
1963 writeQueryField(out, info, type, fieldName, fieldNameInQuery);
1964 }
1965 for (MyReferenceMember member : info.getReferenceMembers()) {
1966 writeQueryReferenceField(out, info, member);
1967 }
1968 out.format(" }\n\n");
1969 out.format(" public static %s<%s> select(%s<%s> where) {\n",
1970 info.addType(SelectBuilder.class), info.getJavaClassSimpleName(),
1971 info.addType(BooleanExpression.class), info.getJavaClassSimpleName());
1972 out.format(
1973 " return new %s<%s>(where).entityClass(%s.class).info(signaller.getInfo());\n",
1974 info.addType(SelectBuilder.class), info.getJavaClassSimpleName(),
1975 info.getJavaClassSimpleName());
1976 out.format(" }\n\n");
1977
1978 out.format(" public static %s<%s> select() {\n", info.addType(SelectBuilder.class),
1979 info.getJavaClassSimpleName());
1980 out.format(" return select(null);\n");
1981 out.format(" }\n\n");
1982
1983 }
1984
1985 private void writeQueryReferenceField(PrintStream out, ClassInfo info,
1986 MyReferenceMember member) {
1987 for (OtherId id : member.getOtherIds()) {
1988 writeQueryField(out, info, id.getType().getMyType(),
1989 member.getFieldName() + "_" + id.getFieldName(),
1990 member.getFieldName() + "." + id.getFieldName());
1991 }
1992 }
1993
1994 private void writeQueryField(PrintStream out, ClassInfo info, MyType type, String fieldName,
1995 String fieldNameInQuery) {
1996 if (type == MyType.REAL || type == MyType.INTEGER) {
1997 out.format(
1998 " public static final %1$s<%3$s> %2$s = new %1$s<%3$s>(\n new %4$s(\"%5$s\"));\n",
1999 info.addType(NumericExpressionField.class), fieldName,
2000 info.getJavaClassSimpleName(), Field.class.getName(), fieldNameInQuery);
2001 } else {
2002 out.format(
2003 " public static final %1$s<%3$s> %2$s = new %1$s<%3$s>(\n new %4$s(\"%5$s\"));\n",
2004 info.addType(StringExpressionField.class), fieldName,
2005 info.getJavaClassSimpleName(), Field.class.getName(), fieldNameInQuery);
2006 }
2007 }
2008 }