1 package org.codehaus.modello.plugin.java;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 import javax.inject.Named;
26
27 import java.io.IOException;
28 import java.io.Serializable;
29 import java.util.Arrays;
30 import java.util.Collection;
31 import java.util.HashSet;
32 import java.util.Iterator;
33 import java.util.List;
34 import java.util.Objects;
35 import java.util.Properties;
36 import java.util.Set;
37
38 import org.codehaus.modello.ModelloException;
39 import org.codehaus.modello.ModelloRuntimeException;
40 import org.codehaus.modello.model.CodeSegment;
41 import org.codehaus.modello.model.Model;
42 import org.codehaus.modello.model.ModelAssociation;
43 import org.codehaus.modello.model.ModelClass;
44 import org.codehaus.modello.model.ModelDefault;
45 import org.codehaus.modello.model.ModelField;
46 import org.codehaus.modello.model.ModelInterface;
47 import org.codehaus.modello.plugin.java.javasource.JArrayType;
48 import org.codehaus.modello.plugin.java.javasource.JClass;
49 import org.codehaus.modello.plugin.java.javasource.JCollectionType;
50 import org.codehaus.modello.plugin.java.javasource.JConstructor;
51 import org.codehaus.modello.plugin.java.javasource.JDocDescriptor;
52 import org.codehaus.modello.plugin.java.javasource.JField;
53 import org.codehaus.modello.plugin.java.javasource.JInterface;
54 import org.codehaus.modello.plugin.java.javasource.JMapType;
55 import org.codehaus.modello.plugin.java.javasource.JMethod;
56 import org.codehaus.modello.plugin.java.javasource.JMethodSignature;
57 import org.codehaus.modello.plugin.java.javasource.JParameter;
58 import org.codehaus.modello.plugin.java.javasource.JSourceCode;
59 import org.codehaus.modello.plugin.java.javasource.JSourceWriter;
60 import org.codehaus.modello.plugin.java.javasource.JType;
61 import org.codehaus.modello.plugin.java.metadata.JavaAssociationMetadata;
62 import org.codehaus.modello.plugin.java.metadata.JavaClassMetadata;
63 import org.codehaus.modello.plugin.java.metadata.JavaFieldMetadata;
64 import org.codehaus.modello.plugin.model.ModelClassMetadata;
65 import org.codehaus.plexus.util.StringUtils;
66
67
68
69
70 @Named("java")
71 public class JavaModelloGenerator extends AbstractJavaModelloGenerator {
72
73 private Collection<String> immutableTypes = new HashSet<String>(Arrays.asList(new String[] {
74 "boolean",
75 "Boolean",
76 "byte",
77 "Byte",
78 "char",
79 "Character",
80 "short",
81 "Short",
82 "int",
83 "Integer",
84 "long",
85 "Long",
86 "float",
87 "Float",
88 "double",
89 "Double",
90 "String"
91 }));
92
93 public void generate(Model model, Properties parameters) throws ModelloException {
94 initialize(model, parameters);
95
96 try {
97 generateJava();
98 } catch (IOException ex) {
99 throw new ModelloException("Exception while generating Java.", ex);
100 }
101 }
102
103 private void generateJava() throws ModelloException, IOException {
104 Model objectModel = getModel();
105
106 ModelClass locationTrackerClass = objectModel.getLocationTracker(getGeneratedVersion());
107 ModelClass sourceTrackerClass = objectModel.getSourceTracker(getGeneratedVersion());
108
109
110
111
112
113 for (ModelInterface modelInterface : objectModel.getInterfaces(getGeneratedVersion())) {
114 generateInterface(modelInterface);
115 }
116
117 String locationTrackerInterface = generateLocationTracker(objectModel, locationTrackerClass);
118
119
120
121
122
123 for (ModelClass modelClass : objectModel.getClasses(getGeneratedVersion())) {
124 JavaClassMetadata javaClassMetadata = (JavaClassMetadata) modelClass.getMetadata(JavaClassMetadata.ID);
125
126 if (!javaClassMetadata.isEnabled()) {
127
128 continue;
129 }
130
131 String packageName = modelClass.getPackageName(isPackageWithVersion(), getGeneratedVersion());
132
133 JSourceWriter sourceWriter = newJSourceWriter(packageName, modelClass.getName());
134
135 JClass jClass = new JClass(packageName + '.' + modelClass.getName());
136
137 initHeader(jClass);
138
139 suppressAllWarnings(objectModel, jClass);
140
141 if (StringUtils.isNotEmpty(modelClass.getDescription())) {
142 jClass.getJDocComment().setComment(appendPeriod(modelClass.getDescription()));
143 }
144
145 addModelImports(jClass, modelClass);
146
147 jClass.getModifiers().setAbstract(javaClassMetadata.isAbstract());
148
149 boolean superClassInModel = false;
150 if (modelClass.getSuperClass() != null) {
151 jClass.setSuperClass(modelClass.getSuperClass());
152 superClassInModel = isClassInModel(modelClass.getSuperClass(), objectModel);
153 }
154
155 for (String implementedInterface : modelClass.getInterfaces()) {
156 jClass.addInterface(implementedInterface);
157 }
158
159 jClass.addInterface(Serializable.class.getName());
160
161 if (hasJavaSourceSupport(5) && !modelClass.getAnnotations().isEmpty()) {
162 for (String annotation : modelClass.getAnnotations()) {
163 jClass.appendAnnotation(annotation);
164 }
165 }
166
167 JSourceCode jConstructorSource = new JSourceCode();
168
169 for (ModelField modelField : modelClass.getFields(getGeneratedVersion())) {
170 if (modelField instanceof ModelAssociation) {
171 createAssociation(jClass, (ModelAssociation) modelField, jConstructorSource);
172 } else {
173 createField(jClass, modelField);
174 }
175 }
176
177
178
179 JConstructor jConstructor = null;
180
181 if (!jConstructorSource.isEmpty()) {
182
183 jConstructor = jClass.createConstructor();
184 jConstructor.setSourceCode(jConstructorSource);
185 jClass.addConstructor(jConstructor);
186 }
187
188
189
190
191
192 List<ModelField> identifierFields = modelClass.getIdentifierFields(getGeneratedVersion());
193
194 if (identifierFields.size() != 0) {
195 JMethod equals = generateEquals(modelClass);
196
197 jClass.addMethod(equals);
198
199 JMethod hashCode = generateHashCode(modelClass);
200
201 jClass.addMethod(hashCode);
202
203
204 if (!javaClassMetadata.isGenerateToString()) {
205 JMethod toString = generateToString(modelClass, true);
206
207 jClass.addMethod(toString);
208 }
209 }
210
211 if (javaClassMetadata.isGenerateToString()) {
212
213 JMethod toString = generateToString(modelClass, false);
214
215 jClass.addMethod(toString);
216 }
217
218
219
220
221
222
223 if (javaClassMetadata.isGenerateBuilder()) {
224 generateBuilder(modelClass, jClass.createInnerClass("Builder"), jConstructor);
225 }
226
227
228
229
230
231
232 if (javaClassMetadata.isGenerateStaticCreators()) {
233 generateStaticCreator(modelClass, jClass, jConstructor);
234 }
235
236 boolean cloneLocations = !superClassInModel && modelClass != sourceTrackerClass;
237 JMethod[] cloneMethods = generateClone(modelClass, cloneLocations ? locationTrackerClass : null);
238 if (cloneMethods.length > 0) {
239 jClass.addInterface(Cloneable.class.getName());
240 jClass.addMethods(cloneMethods);
241 }
242
243 if (modelClass.getCodeSegments(getGeneratedVersion()) != null) {
244 for (CodeSegment codeSegment : modelClass.getCodeSegments(getGeneratedVersion())) {
245 jClass.addSourceCode(codeSegment.getCode());
246 }
247 }
248
249 ModelClassMetadata modelClassMetadata = (ModelClassMetadata) modelClass.getMetadata(ModelClassMetadata.ID);
250
251 if (modelClassMetadata != null) {
252 if (modelClassMetadata.isRootElement()) {
253 ModelField modelEncoding = new ModelField(modelClass, "modelEncoding");
254 modelEncoding.setType("String");
255 modelEncoding.setDefaultValue("UTF-8");
256 modelEncoding.addMetadata(new JavaFieldMetadata());
257 createField(jClass, modelEncoding);
258 }
259 }
260
261 if (modelClass == locationTrackerClass) {
262 jClass.addInterface(locationTrackerInterface);
263
264 generateLocationBean(jClass, modelClass, sourceTrackerClass);
265
266 generateLocationTracking(jClass, modelClass, locationTrackerClass);
267 } else if (locationTrackerClass != null && modelClass != sourceTrackerClass && !superClassInModel) {
268 jClass.addInterface(locationTrackerInterface);
269
270 generateLocationTracking(jClass, modelClass, locationTrackerClass);
271 }
272
273 jClass.print(sourceWriter);
274
275 sourceWriter.close();
276 }
277 }
278
279 private void generateInterface(ModelInterface modelInterface) throws ModelloException, IOException {
280 Model objectModel = modelInterface.getModel();
281
282 String packageName = modelInterface.getPackageName(isPackageWithVersion(), getGeneratedVersion());
283
284 JSourceWriter sourceWriter = newJSourceWriter(packageName, modelInterface.getName());
285
286 JInterface jInterface = new JInterface(packageName + '.' + modelInterface.getName());
287
288 initHeader(jInterface);
289
290 suppressAllWarnings(objectModel, jInterface);
291
292 if (modelInterface.getSuperInterface() != null) {
293
294 try {
295 ModelInterface superInterface =
296 objectModel.getInterface(modelInterface.getSuperInterface(), getGeneratedVersion());
297 String superPackageName = superInterface.getPackageName(isPackageWithVersion(), getGeneratedVersion());
298
299 if (!packageName.equals(superPackageName)) {
300 jInterface.addImport(superPackageName + '.' + superInterface.getName());
301 }
302 } catch (ModelloRuntimeException mre) {
303
304 }
305
306 jInterface.addInterface(modelInterface.getSuperInterface());
307 }
308
309 if (modelInterface.getCodeSegments(getGeneratedVersion()) != null) {
310 for (CodeSegment codeSegment : modelInterface.getCodeSegments(getGeneratedVersion())) {
311 jInterface.addSourceCode(codeSegment.getCode());
312 }
313 }
314
315 if (hasJavaSourceSupport(5) && !modelInterface.getAnnotations().isEmpty()) {
316 for (String annotation : modelInterface.getAnnotations()) {
317 jInterface.appendAnnotation(annotation);
318 }
319 }
320
321 jInterface.print(sourceWriter);
322
323 sourceWriter.close();
324 }
325
326 private JMethod generateEquals(ModelClass modelClass) {
327 JMethod equals = new JMethod("equals", JType.BOOLEAN, null);
328
329 equals.addParameter(new JParameter(new JClass("Object"), "other"));
330
331 JSourceCode sc = equals.getSourceCode();
332
333 sc.add("if ( this == other )");
334 sc.add("{");
335 sc.addIndented("return true;");
336 sc.add("}");
337 sc.add("");
338 sc.add("if ( !( other instanceof " + modelClass.getName() + " ) )");
339 sc.add("{");
340 sc.addIndented("return false;");
341 sc.add("}");
342 sc.add("");
343 sc.add(modelClass.getName() + " that = (" + modelClass.getName() + ") other;");
344 sc.add("boolean result = true;");
345
346 sc.add("");
347
348 for (ModelField identifier : modelClass.getIdentifierFields(getGeneratedVersion())) {
349 String name = identifier.getName();
350 if ("boolean".equals(identifier.getType())
351 || "byte".equals(identifier.getType())
352 || "char".equals(identifier.getType())
353 || "double".equals(identifier.getType())
354 || "float".equals(identifier.getType())
355 || "int".equals(identifier.getType())
356 || "short".equals(identifier.getType())
357 || "long".equals(identifier.getType())) {
358 sc.add("result = result && " + name + " == that." + name + ";");
359 } else {
360 name = "get" + capitalise(name) + "()";
361 sc.add("result = result && ( " + name + " == null ? that." + name + " == null : " + name
362 + ".equals( that." + name + " ) );");
363 }
364 }
365
366 if (modelClass.getSuperClass() != null) {
367 sc.add("result = result && ( super.equals( other ) );");
368 }
369
370 sc.add("");
371
372 sc.add("return result;");
373
374 return equals;
375 }
376
377 private JMethod generateToString(ModelClass modelClass, boolean onlyIdentifierFields) {
378 JMethod toString = new JMethod("toString", new JType(String.class.getName()), null);
379
380 List<ModelField> fields = onlyIdentifierFields
381 ? modelClass.getIdentifierFields(getGeneratedVersion())
382 : modelClass.getFields(getGeneratedVersion());
383
384 JSourceCode sc = toString.getSourceCode();
385
386 if (fields.size() == 0) {
387 sc.add("return super.toString();");
388
389 return toString;
390 }
391
392 sc.add("StringBuilder buf = new StringBuilder( 128 );");
393
394 sc.add("");
395
396 for (Iterator<ModelField> j = fields.iterator(); j.hasNext(); ) {
397 ModelField identifier = j.next();
398
399 String getter = "boolean".equals(identifier.getType()) ? "is" : "get";
400
401 sc.add("buf.append( \"" + identifier.getName() + " = '\" );");
402 sc.add("buf.append( " + getter + capitalise(identifier.getName()) + "() );");
403 sc.add("buf.append( \"'\" );");
404
405 if (j.hasNext()) {
406 sc.add("buf.append( \"\\n\" ); ");
407 }
408 }
409
410 if (modelClass.getSuperClass() != null) {
411 sc.add("buf.append( \"\\n\" );");
412 sc.add("buf.append( super.toString() );");
413 }
414
415 sc.add("");
416
417 sc.add("return buf.toString();");
418
419 return toString;
420 }
421
422 private JMethod generateHashCode(ModelClass modelClass) {
423 JMethod hashCode = new JMethod("hashCode", JType.INT, null);
424
425 List<ModelField> identifierFields = modelClass.getIdentifierFields(getGeneratedVersion());
426
427 JSourceCode sc = hashCode.getSourceCode();
428
429 if (identifierFields.size() == 0) {
430 sc.add("return super.hashCode();");
431
432 return hashCode;
433 }
434
435 sc.add("int result = 17;");
436
437 sc.add("");
438
439 for (ModelField identifier : identifierFields) {
440 sc.add("result = 37 * result + " + createHashCodeForField(identifier) + ";");
441 }
442
443 if (modelClass.getSuperClass() != null) {
444 sc.add("result = 37 * result + super.hashCode();");
445 }
446
447 sc.add("");
448
449 sc.add("return result;");
450
451 return hashCode;
452 }
453
454 private JMethod[] generateClone(ModelClass modelClass, ModelClass locationClass) throws ModelloException {
455 String cloneModeClass = getCloneMode(modelClass);
456
457 if (JavaClassMetadata.CLONE_NONE.equals(cloneModeClass)) {
458 return new JMethod[0];
459 }
460
461 boolean useJava5 = hasJavaSourceSupport(5);
462
463 JType returnType;
464 if (useJava5) {
465 returnType = new JClass(modelClass.getName());
466 } else {
467 returnType = new JClass("Object");
468 }
469
470 JMethod cloneMethod = new JMethod("clone", returnType, null);
471
472 JSourceCode sc = cloneMethod.getSourceCode();
473
474 sc.add("try");
475 sc.add("{");
476 sc.indent();
477
478 sc.add(modelClass.getName() + " copy = (" + modelClass.getName() + ") super.clone();");
479
480 sc.add("");
481
482 for (ModelField modelField : modelClass.getFields(getGeneratedVersion())) {
483 String thisField = "this." + modelField.getName();
484 String copyField = "copy." + modelField.getName();
485
486 if ("DOM".equals(modelField.getType())) {
487 sc.add("if ( " + thisField + " != null )");
488 sc.add("{");
489 if (domAsXpp3) {
490 sc.addIndented(copyField
491 + " = new org.codehaus.plexus.util.xml.Xpp3Dom( (org.codehaus.plexus.util.xml.Xpp3Dom) "
492 + thisField + " );");
493 } else {
494 sc.addIndented(copyField + " = ( (org.w3c.dom.Node) " + thisField + ").cloneNode( true );");
495 }
496 sc.add("}");
497 sc.add("");
498 } else if ("Date".equalsIgnoreCase(modelField.getType()) || "java.util.Date".equals(modelField.getType())) {
499 sc.add("if ( " + thisField + " != null )");
500 sc.add("{");
501 sc.addIndented(copyField + " = (java.util.Date) " + thisField + ".clone();");
502 sc.add("}");
503 sc.add("");
504 } else if (ModelDefault.PROPERTIES.equals(modelField.getType())) {
505 sc.add("if ( " + thisField + " != null )");
506 sc.add("{");
507 sc.addIndented(copyField + " = (" + ModelDefault.PROPERTIES + ") " + thisField + ".clone();");
508 sc.add("}");
509 sc.add("");
510 } else if (modelField instanceof ModelAssociation) {
511 ModelAssociation modelAssociation = (ModelAssociation) modelField;
512
513 String cloneModeAssoc = getCloneMode(modelAssociation, cloneModeClass);
514
515 boolean deepClone = JavaAssociationMetadata.CLONE_DEEP.equals(cloneModeAssoc)
516 && !immutableTypes.contains(modelAssociation.getTo());
517
518 if (modelAssociation.isOneMultiplicity()) {
519 if (deepClone) {
520 sc.add("if ( " + thisField + " != null )");
521 sc.add("{");
522 sc.addIndented(copyField + " = (" + modelAssociation.getTo() + ") " + thisField + ".clone();");
523 sc.add("}");
524 sc.add("");
525 }
526 } else {
527 sc.add("if ( " + thisField + " != null )");
528 sc.add("{");
529 sc.indent();
530
531 JavaAssociationMetadata javaAssociationMetadata = getJavaAssociationMetadata(modelAssociation);
532 JType componentType = getComponentType(modelAssociation, javaAssociationMetadata);
533
534 sc.add(copyField + " = " + getDefaultValue(modelAssociation, componentType) + ";");
535
536 if (isCollection(modelField.getType())) {
537 if (deepClone) {
538 if (useJava5) {
539 sc.add("for ( " + componentType.getName() + " item : " + thisField + " )");
540 } else {
541 sc.add("for ( java.util.Iterator it = " + thisField + ".iterator(); it.hasNext(); )");
542 }
543 sc.add("{");
544 sc.indent();
545 if (useJava5) {
546 sc.add(copyField + ".add( ( (" + modelAssociation.getTo() + ") item).clone() );");
547 } else {
548 sc.add(copyField + ".add( ( (" + modelAssociation.getTo() + ") it.next() ).clone() );");
549 }
550 sc.unindent();
551 sc.add("}");
552 } else {
553 sc.add(copyField + ".addAll( " + thisField + " );");
554 }
555 } else if (isMap(modelField.getType())) {
556 sc.add(copyField + ".clear();");
557 sc.add(copyField + ".putAll( " + thisField + " );");
558 }
559
560 sc.unindent();
561 sc.add("}");
562 sc.add("");
563 }
564 }
565 }
566
567 if (locationClass != null) {
568 String locationField =
569 ((ModelClassMetadata) locationClass.getMetadata(ModelClassMetadata.ID)).getLocationTracker();
570 sc.add("if ( copy." + locationField + " != null )");
571 sc.add("{");
572 sc.indent();
573 sc.add("copy." + locationField + " = new java.util.LinkedHashMap" + "( copy." + locationField + " );");
574 sc.unindent();
575 sc.add("}");
576 sc.add("");
577 }
578
579 String cloneHook = getCloneHook(modelClass);
580
581 if (StringUtils.isNotEmpty(cloneHook) && !"false".equalsIgnoreCase(cloneHook)) {
582 if ("true".equalsIgnoreCase(cloneHook)) {
583 cloneHook = "cloneHook";
584 }
585
586 sc.add(cloneHook + "( copy );");
587 sc.add("");
588 }
589
590 sc.add("return copy;");
591
592 sc.unindent();
593 sc.add("}");
594 sc.add("catch ( " + Exception.class.getName() + " ex )");
595 sc.add("{");
596 sc.indent();
597 sc.add("throw (" + RuntimeException.class.getName() + ") new " + UnsupportedOperationException.class.getName()
598 + "( getClass().getName()");
599 sc.addIndented("+ \" does not support clone()\" ).initCause( ex );");
600 sc.unindent();
601 sc.add("}");
602
603 return new JMethod[] {cloneMethod};
604 }
605
606 private String getCloneMode(ModelClass modelClass) throws ModelloException {
607 String cloneMode = null;
608
609 for (ModelClass currentClass = modelClass; ; ) {
610 JavaClassMetadata javaClassMetadata = (JavaClassMetadata) currentClass.getMetadata(JavaClassMetadata.ID);
611
612 cloneMode = javaClassMetadata.getCloneMode();
613
614 if (cloneMode != null) {
615 break;
616 }
617
618 String superClass = currentClass.getSuperClass();
619 if (StringUtils.isEmpty(superClass) || !isClassInModel(superClass, getModel())) {
620 break;
621 }
622
623 currentClass = getModel().getClass(superClass, getGeneratedVersion());
624 }
625
626 if (cloneMode == null) {
627 cloneMode = JavaClassMetadata.CLONE_NONE;
628 } else if (!JavaClassMetadata.CLONE_MODES.contains(cloneMode)) {
629 throw new ModelloException("The Java Modello Generator cannot use '" + cloneMode
630 + "' as a value for <class java.clone=\"...\">, " + "only the following values are acceptable "
631 + JavaClassMetadata.CLONE_MODES);
632 }
633
634 return cloneMode;
635 }
636
637 private String getCloneMode(ModelAssociation modelAssociation, String cloneModeClass) throws ModelloException {
638 JavaAssociationMetadata javaAssociationMetadata =
639 (JavaAssociationMetadata) modelAssociation.getAssociationMetadata(JavaAssociationMetadata.ID);
640
641 String cloneModeAssoc = javaAssociationMetadata.getCloneMode();
642 if (cloneModeAssoc == null) {
643 cloneModeAssoc = cloneModeClass;
644 } else if (!JavaAssociationMetadata.CLONE_MODES.contains(cloneModeAssoc)) {
645 throw new ModelloException("The Java Modello Generator cannot use '" + cloneModeAssoc
646 + "' as a value for <association java.clone=\"...\">, "
647 + "only the following values are acceptable "
648 + JavaAssociationMetadata.CLONE_MODES);
649 }
650
651 return cloneModeAssoc;
652 }
653
654 private String getCloneHook(ModelClass modelClass) throws ModelloException {
655 JavaClassMetadata javaClassMetadata = (JavaClassMetadata) modelClass.getMetadata(JavaClassMetadata.ID);
656
657 return javaClassMetadata.getCloneHook();
658 }
659
660 private String generateLocationTracker(Model objectModel, ModelClass locationClass)
661 throws ModelloException, IOException {
662 if (locationClass == null) {
663 return null;
664 }
665
666 String locationField =
667 ((ModelClassMetadata) locationClass.getMetadata(ModelClassMetadata.ID)).getLocationTracker();
668
669 String propertyName = capitalise(singular(locationField));
670
671 String interfaceName = locationClass.getName() + "Tracker";
672
673 String packageName = locationClass.getPackageName(isPackageWithVersion(), getGeneratedVersion());
674
675 JSourceWriter sourceWriter = newJSourceWriter(packageName, interfaceName);
676
677 JInterface jInterface = new JInterface(packageName + '.' + interfaceName);
678
679 initHeader(jInterface);
680
681 suppressAllWarnings(objectModel, jInterface);
682
683 JMethodSignature jMethod = new JMethodSignature("get" + propertyName, new JType(locationClass.getName()));
684 jMethod.setComment("Gets the location of the specified field in the input source.");
685 addParameter(jMethod, "Object", "field", "The key of the field, must not be <code>null</code>.");
686 String returnDoc = "The location of the field in the input source or <code>null</code> if unknown.";
687 jMethod.getJDocComment().addDescriptor(JDocDescriptor.createReturnDesc(returnDoc));
688 jInterface.addMethod(jMethod);
689
690 jMethod = new JMethodSignature("set" + propertyName, null);
691 jMethod.setComment("Sets the location of the specified field.");
692 addParameter(jMethod, "Object", "field", "The key of the field, must not be <code>null</code>.");
693 addParameter(
694 jMethod,
695 locationClass.getName(),
696 singular(locationField),
697 "The location of the field, may be <code>null</code>.");
698 jInterface.addMethod(jMethod);
699
700 jInterface.print(sourceWriter);
701
702 sourceWriter.close();
703
704 return jInterface.getName();
705 }
706
707 private void generateLocationTracking(JClass jClass, ModelClass modelClass, ModelClass locationClass)
708 throws ModelloException {
709 if (locationClass == null) {
710 return;
711 }
712
713 String superClass = modelClass.getSuperClass();
714 ModelClassMetadata metadata = (ModelClassMetadata) locationClass.getMetadata(ModelClassMetadata.ID);
715 String locationField = metadata.getLocationTracker();
716 boolean hasModeSuperClass = StringUtils.isNotEmpty(superClass) && isClassInModel(superClass, getModel());
717 if (!hasModeSuperClass) {
718 boolean useJava5 = hasJavaSourceSupport(5);
719 String fieldType = "java.util.Map" + (useJava5 ? "<Object, " + locationClass.getName() + ">" : "");
720 String fieldImpl =
721 "java.util.LinkedHashMap" + (useJava5 ? "<Object, " + locationClass.getName() + ">" : "");
722
723
724 JField jField = new JField(new JType(fieldType), locationField);
725 jClass.addField(jField);
726
727
728 JMethod getter = new JMethod(
729 "getOther" + capitalise(singular(locationField)), new JType(locationClass.getName()), null);
730 getter.addParameter(new JParameter(new JType("Object"), "key"));
731 getter.getModifiers().makePrivate();
732 JSourceCode getterSc = getter.getSourceCode();
733 getterSc.add("return ( " + locationField + " != null ) ? " + locationField + ".get( key ) : null;");
734 getter.setComment("");
735 jClass.addMethod(getter);
736
737
738 JMethod setter = new JMethod("setOther" + capitalise(singular(locationField)));
739 setter.addParameter(new JParameter(new JType("Object"), "key"));
740 setter.addParameter(new JParameter(new JType(locationClass.getName()), singular(locationField)));
741 JSourceCode setterSc = setter.getSourceCode();
742 setterSc.add("if ( " + singular(locationField) + " != null )");
743 setterSc.add("{");
744 setterSc.indent();
745 setterSc.add("if ( this." + locationField + " == null )");
746 setterSc.add("{");
747 setterSc.addIndented("this." + locationField + " = new " + fieldImpl + "();");
748 setterSc.add("}");
749 setterSc.add("this." + locationField + ".put( key, " + singular(locationField) + " );");
750 setterSc.unindent();
751 setterSc.add("}");
752 setter.setComment("");
753 jClass.addMethod(setter);
754 }
755
756 JField ownLocation = new JField(new JType(locationClass.getName()), singular(locationField));
757 jClass.addField(ownLocation);
758 for (ModelField field : modelClass.getAllFields()) {
759 JField fieldLocation = new JField(
760 new JType(locationClass.getName()), field.getName() + capitalise(singular(locationField)));
761 jClass.addField(fieldLocation);
762 }
763
764
765 JMethod getter =
766 new JMethod("get" + capitalise(singular(locationField)), new JType(locationClass.getName()), null);
767 getter.addParameter(new JParameter(new JType("Object"), "key"));
768 JSourceCode getterSc = getter.getSourceCode();
769
770 getterSc.add("if ( key instanceof String )");
771 getterSc.add("{");
772 getterSc.indent();
773 if (hasJavaSourceSupport(7)) {
774 getterSc.add("switch ( ( String ) key )");
775 getterSc.add("{");
776 getterSc.indent();
777 getterSc.add("case \"\" :");
778 getterSc.add("{");
779 getterSc.indent();
780 getterSc.add("return this." + singular(locationField) + ";");
781 getterSc.unindent();
782 getterSc.add("}");
783 for (ModelField field : modelClass.getAllFields()) {
784 getterSc.add("case \"" + field.getName() + "\" :");
785 getterSc.add("{");
786 getterSc.indent();
787 getterSc.add("return " + field.getName() + capitalise(singular(locationField)) + ";");
788 getterSc.unindent();
789 getterSc.add("}");
790 }
791 getterSc.add("default :");
792 getterSc.add("{");
793 getterSc.indent();
794 if (hasModeSuperClass) {
795 getterSc.add("return super.get" + capitalise(singular(locationField)) + "( key );");
796 } else {
797 getterSc.add("return getOther" + capitalise(singular(locationField)) + "( key );");
798 }
799 getterSc.unindent();
800 getterSc.add("}");
801 getterSc.add("}");
802 } else {
803 getterSc.add("if ( \"\".equals( key ) )");
804 getterSc.add("{");
805 getterSc.indent();
806 getterSc.add("return this." + singular(locationField) + ";");
807 getterSc.unindent();
808 getterSc.add("}");
809 for (ModelField field : modelClass.getAllFields()) {
810 getterSc.add("else if ( \"" + field.getName() + "\".equals( key ) )");
811 getterSc.add("{");
812 getterSc.indent();
813 getterSc.add("return " + field.getName() + capitalise(singular(locationField)) + ";");
814 getterSc.unindent();
815 getterSc.add("}");
816 }
817 getterSc.add("else");
818 getterSc.add("{");
819 getterSc.indent();
820 if (hasModeSuperClass) {
821 getterSc.add("return super.get" + capitalise(singular(locationField)) + "( key );");
822 } else {
823 getterSc.add("return getOther" + capitalise(singular(locationField)) + "( key );");
824 }
825 getterSc.unindent();
826 getterSc.add("}");
827 }
828 getterSc.unindent();
829 getterSc.add("}");
830 getterSc.add("else");
831 getterSc.add("{");
832 getterSc.indent();
833 if (hasModeSuperClass) {
834 getterSc.add("return super.get" + capitalise(singular(locationField)) + "( key );");
835 } else {
836 getterSc.add("return getOther" + capitalise(singular(locationField)) + "( key );");
837 }
838 getterSc.unindent();
839 getterSc.add("}");
840
841 getter.setComment("");
842 jClass.addMethod(getter);
843
844
845 JMethod setter = new JMethod("set" + capitalise(singular(locationField)));
846 setter.addParameter(new JParameter(new JType("Object"), "key"));
847 setter.addParameter(new JParameter(new JType(locationClass.getName()), singular(locationField)));
848 JSourceCode setterSc = setter.getSourceCode();
849 setterSc.add("if ( key instanceof String )");
850 setterSc.add("{");
851 setterSc.indent();
852 if (hasJavaSourceSupport(7)) {
853 setterSc.add("switch ( ( String ) key )");
854 setterSc.add("{");
855 setterSc.indent();
856 setterSc.add("case \"\" :");
857 setterSc.add("{");
858 setterSc.indent();
859 setterSc.add("this." + singular(locationField) + " = " + singular(locationField) + ";");
860 setterSc.add("return;");
861 setterSc.unindent();
862 setterSc.add("}");
863 for (ModelField field : modelClass.getAllFields()) {
864 setterSc.add("case \"" + field.getName() + "\" :");
865 setterSc.add("{");
866 setterSc.indent();
867 setterSc.add(
868 field.getName() + capitalise(singular(locationField)) + " = " + singular(locationField) + ";");
869 setterSc.add("return;");
870 setterSc.unindent();
871 setterSc.add("}");
872 }
873 setterSc.add("default :");
874 setterSc.add("{");
875 setterSc.indent();
876 if (hasModeSuperClass) {
877 setterSc.add("super.set" + capitalise(singular(locationField)) + "( key, " + singular(locationField)
878 + " );");
879 } else {
880 setterSc.add(
881 "setOther" + capitalise(singular(locationField)) + "( key, " + singular(locationField) + " );");
882 }
883 setterSc.add("return;");
884 setterSc.unindent();
885 setterSc.add("}");
886 setterSc.unindent();
887 setterSc.add("}");
888 } else {
889 setterSc.add("if ( \"\".equals( key ) )");
890 setterSc.add("{");
891 setterSc.indent();
892 setterSc.add("this." + singular(locationField) + " = " + singular(locationField) + ";");
893 setterSc.add("return;");
894 setterSc.unindent();
895 setterSc.add("}");
896 for (ModelField field : modelClass.getAllFields()) {
897 setterSc.add("else if ( \"" + field.getName() + "\".equals( key ) )");
898 setterSc.add("{");
899 setterSc.indent();
900 setterSc.add(
901 field.getName() + capitalise(singular(locationField)) + " = " + singular(locationField) + ";");
902 setterSc.add("return;");
903 setterSc.unindent();
904 setterSc.add("}");
905 }
906 setterSc.add("else");
907 setterSc.add("{");
908 setterSc.indent();
909 if (hasModeSuperClass) {
910 setterSc.add("super.set" + capitalise(singular(locationField)) + "( key, " + singular(locationField)
911 + " );");
912 } else {
913 setterSc.add(
914 "setOther" + capitalise(singular(locationField)) + "( key, " + singular(locationField) + " );");
915 }
916 setterSc.add("return;");
917 setterSc.unindent();
918 setterSc.add("}");
919 }
920
921 setterSc.unindent();
922 setterSc.add("}");
923 setterSc.add("else");
924 setterSc.add("{");
925 setterSc.indent();
926 if (hasModeSuperClass) {
927 setterSc.add(
928 "super.set" + capitalise(singular(locationField)) + "( key, " + singular(locationField) + " );");
929 } else {
930 setterSc.add(
931 "setOther" + capitalise(singular(locationField)) + "( key, " + singular(locationField) + " );");
932 }
933 setterSc.unindent();
934 setterSc.add("}");
935
936 setter.setComment("");
937 jClass.addMethod(setter);
938 }
939
940 private void generateLocationBean(JClass jClass, ModelClass locationClass, ModelClass sourceClass)
941 throws ModelloException {
942 jClass.getModifiers().setFinal(true);
943
944 String locationsField =
945 ((ModelClassMetadata) locationClass.getMetadata(ModelClassMetadata.ID)).getLocationTracker();
946
947 JavaFieldMetadata readOnlyField = new JavaFieldMetadata();
948 readOnlyField.setSetter(false);
949
950
951 ModelField lineNumber = new ModelField(locationClass, "lineNumber");
952 lineNumber.setDescription("The one-based line number. The value will be non-positive if unknown.");
953 lineNumber.setType("int");
954 lineNumber.setDefaultValue("-1");
955 lineNumber.addMetadata(readOnlyField);
956 createField(jClass, lineNumber);
957
958
959 ModelField columnNumber = new ModelField(locationClass, "columnNumber");
960 columnNumber.setDescription("The one-based column number. The value will be non-positive if unknown.");
961 columnNumber.setType("int");
962 columnNumber.setDefaultValue("-1");
963 columnNumber.addMetadata(readOnlyField);
964 createField(jClass, columnNumber);
965
966
967 ModelField source = null;
968 if (sourceClass != null) {
969 ModelClassMetadata metadata = (ModelClassMetadata) sourceClass.getMetadata(ModelClassMetadata.ID);
970 String sourceField = metadata.getSourceTracker();
971
972 source = new ModelField(locationClass, sourceField);
973 source.setType(sourceClass.getName());
974 source.addMetadata(readOnlyField);
975 createField(jClass, source);
976 }
977
978
979 JConstructor jConstructor = jClass.createConstructor();
980 JSourceCode sc = jConstructor.getSourceCode();
981
982 jConstructor.addParameter(new JParameter(JType.INT, lineNumber.getName()));
983 sc.add("this." + lineNumber.getName() + " = " + lineNumber.getName() + ";");
984
985 jConstructor.addParameter(new JParameter(JType.INT, columnNumber.getName()));
986 sc.add("this." + columnNumber.getName() + " = " + columnNumber.getName() + ";");
987
988
989 if (sourceClass != null) {
990 jConstructor = jClass.createConstructor(jConstructor.getParameters());
991 sc.copyInto(jConstructor.getSourceCode());
992 sc = jConstructor.getSourceCode();
993
994 jConstructor.addParameter(new JParameter(new JType(sourceClass.getName()), source.getName()));
995 sc.add("this." + source.getName() + " = " + source.getName() + ";");
996 }
997
998 boolean useJava5 = hasJavaSourceSupport(5);
999 JType fieldType = new JMapType("java.util.Map", new JType(locationClass.getName()), useJava5);
1000 JType fieldImpl = new JMapType("java.util.LinkedHashMap", new JType(locationClass.getName()), useJava5);
1001
1002
1003 JMethod jMethod = new JMethod("get" + capitalise(locationsField), fieldType, null);
1004 sc = jMethod.getSourceCode();
1005 sc.add("return " + locationsField + ";");
1006 jMethod.setComment("");
1007 jClass.addMethod(jMethod);
1008
1009
1010 jMethod = new JMethod("set" + capitalise(locationsField));
1011 jMethod.addParameter(new JParameter(fieldType, locationsField));
1012 sc = jMethod.getSourceCode();
1013 sc.add("this." + locationsField + " = " + locationsField + ";");
1014 jMethod.setComment("");
1015 jClass.addMethod(jMethod);
1016
1017
1018 jMethod = new JMethod("merge", new JType(locationClass.getName()), null);
1019 jMethod.getModifiers().setStatic(true);
1020 jMethod.addParameter(new JParameter(new JType(locationClass.getName()), "target"));
1021 jMethod.addParameter(new JParameter(new JType(locationClass.getName()), "source"));
1022 jMethod.addParameter(new JParameter(JType.BOOLEAN, "sourceDominant"));
1023 sc = jMethod.getSourceCode();
1024 sc.add("if ( source == null )");
1025 sc.add("{");
1026 sc.addIndented("return target;");
1027 sc.add("}");
1028 sc.add("else if ( target == null )");
1029 sc.add("{");
1030 sc.addIndented("return source;");
1031 sc.add("}");
1032 sc.add("");
1033 sc.add(locationClass.getName() + " result =");
1034 sc.add(" new " + locationClass.getName() + "( target.getLineNumber(), target.getColumnNumber()"
1035 + (sourceClass != null ? ", target.get" + capitalise(source.getName()) + "()" : "") + " );");
1036 sc.add("");
1037 sc.add(fieldType + " locations;");
1038 sc.add(fieldType + " sourceLocations = source.get" + capitalise(locationsField) + "();");
1039 sc.add(fieldType + " targetLocations = target.get" + capitalise(locationsField) + "();");
1040 sc.add("if ( sourceLocations == null )");
1041 sc.add("{");
1042 sc.addIndented("locations = targetLocations;");
1043 sc.add("}");
1044 sc.add("else if ( targetLocations == null )");
1045 sc.add("{");
1046 sc.addIndented("locations = sourceLocations;");
1047 sc.add("}");
1048 sc.add("else");
1049 sc.add("{");
1050 sc.addIndented("locations = new " + fieldImpl.getName() + "();");
1051 sc.addIndented("locations.putAll( sourceDominant ? targetLocations : sourceLocations );");
1052 sc.addIndented("locations.putAll( sourceDominant ? sourceLocations : targetLocations );");
1053 sc.add("}");
1054 sc.add("result.set" + capitalise(locationsField) + "( locations );");
1055 sc.add("");
1056 sc.add("return result;");
1057 jClass.addMethod(jMethod);
1058
1059
1060 jMethod = new JMethod("merge", new JType(locationClass.getName()), null);
1061 jMethod.getModifiers().setStatic(true);
1062 jMethod.addParameter(new JParameter(new JType(locationClass.getName()), "target"));
1063 jMethod.addParameter(new JParameter(new JType(locationClass.getName()), "source"));
1064 jMethod.addParameter(
1065 new JParameter(new JCollectionType("java.util.Collection", new JType("Integer"), useJava5), "indices"));
1066 String intWrap = useJava5 ? "Integer.valueOf" : "new Integer";
1067 sc = jMethod.getSourceCode();
1068 sc.add("if ( source == null )");
1069 sc.add("{");
1070 sc.addIndented("return target;");
1071 sc.add("}");
1072 sc.add("else if ( target == null )");
1073 sc.add("{");
1074 sc.addIndented("return source;");
1075 sc.add("}");
1076 sc.add("");
1077 sc.add(locationClass.getName() + " result =");
1078 sc.add(" new " + locationClass.getName() + "( target.getLineNumber(), target.getColumnNumber()"
1079 + (sourceClass != null ? ", target.get" + capitalise(source.getName()) + "()" : "") + " );");
1080 sc.add("");
1081 sc.add(fieldType + " locations;");
1082 sc.add(fieldType + " sourceLocations = source.get" + capitalise(locationsField) + "();");
1083 sc.add(fieldType + " targetLocations = target.get" + capitalise(locationsField) + "();");
1084 sc.add("if ( sourceLocations == null )");
1085 sc.add("{");
1086 sc.addIndented("locations = targetLocations;");
1087 sc.add("}");
1088 sc.add("else if ( targetLocations == null )");
1089 sc.add("{");
1090 sc.addIndented("locations = sourceLocations;");
1091 sc.add("}");
1092 sc.add("else");
1093 sc.add("{");
1094 sc.indent();
1095 sc.add("locations = new " + fieldImpl + "();");
1096 sc.add("for ( java.util.Iterator" + (useJava5 ? "<Integer>" : "")
1097 + " it = indices.iterator(); it.hasNext(); )");
1098 sc.add("{");
1099 sc.indent();
1100 sc.add(locationClass.getName() + " location;");
1101 sc.add("Integer index = " + (useJava5 ? "" : "(Integer) ") + "it.next();");
1102 sc.add("if ( index.intValue() < 0 )");
1103 sc.add("{");
1104 sc.addIndented("location = sourceLocations.get( " + intWrap + "( ~index.intValue() ) );");
1105 sc.add("}");
1106 sc.add("else");
1107 sc.add("{");
1108 sc.addIndented("location = targetLocations.get( index );");
1109 sc.add("}");
1110 sc.add("locations.put( " + intWrap + "( locations.size() ), location );");
1111 sc.unindent();
1112 sc.add("}");
1113 sc.unindent();
1114 sc.add("}");
1115 sc.add("result.set" + capitalise(locationsField) + "( locations );");
1116 sc.add("");
1117 sc.add("return result;");
1118 jClass.addMethod(jMethod);
1119
1120 JClass stringFormatterClass = jClass.createInnerClass("StringFormatter");
1121 stringFormatterClass.getModifiers().setStatic(true);
1122 stringFormatterClass.getModifiers().setAbstract(true);
1123
1124 jMethod = new JMethod("toString", new JType("String"), null);
1125 jMethod.getModifiers().setAbstract(true);
1126 jMethod.addParameter(new JParameter(new JType(locationClass.getName()), "location"));
1127 stringFormatterClass.addMethod(jMethod);
1128 }
1129
1130
1131
1132
1133
1134
1135
1136
1137 private String appendPeriod(String string) {
1138 if (string == null) {
1139 return string;
1140 }
1141
1142 String trimmedString = string.trim();
1143 if (trimmedString.endsWith(".")
1144 || trimmedString.endsWith("!")
1145 || trimmedString.endsWith("?")
1146 || trimmedString.endsWith(">")) {
1147 return string;
1148 } else {
1149 return string + ".";
1150 }
1151 }
1152
1153 private String createHashCodeForField(ModelField identifier) {
1154 String name = identifier.getName();
1155 String type = identifier.getType();
1156
1157 if ("boolean".equals(type)) {
1158 return "( " + name + " ? 0 : 1 )";
1159 } else if ("byte".equals(type) || "char".equals(type) || "short".equals(type) || "int".equals(type)) {
1160 return "(int) " + name;
1161 } else if ("long".equals(type)) {
1162 return "(int) ( " + name + " ^ ( " + name + " >>> 32 ) )";
1163 } else if ("float".equals(type)) {
1164 return "Float.floatToIntBits( " + name + " )";
1165 } else if ("double".equals(type)) {
1166 return "(int) ( Double.doubleToLongBits( " + identifier.getName() + " ) ^ ( Double.doubleToLongBits( "
1167 + identifier.getName() + " ) >>> 32 ) )";
1168 } else {
1169 return "( " + name + " != null ? " + name + ".hashCode() : 0 )";
1170 }
1171 }
1172
1173 private JField createField(ModelField modelField) throws ModelloException {
1174 JType type;
1175
1176 String baseType = modelField.getType();
1177 if (modelField.isArray()) {
1178
1179 baseType = baseType.substring(0, baseType.length() - 2);
1180 }
1181
1182 if ("boolean".equals(baseType)) {
1183 type = JType.BOOLEAN;
1184 } else if ("byte".equals(baseType)) {
1185 type = JType.BYTE;
1186 } else if ("char".equals(baseType)) {
1187 type = JType.CHAR;
1188 } else if ("double".equals(baseType)) {
1189 type = JType.DOUBLE;
1190 } else if ("float".equals(baseType)) {
1191 type = JType.FLOAT;
1192 } else if ("int".equals(baseType)) {
1193 type = JType.INT;
1194 } else if ("short".equals(baseType)) {
1195 type = JType.SHORT;
1196 } else if ("long".equals(baseType)) {
1197 type = JType.LONG;
1198 } else if ("Date".equals(baseType)) {
1199 type = new JClass("java.util.Date");
1200 } else if ("DOM".equals(baseType)) {
1201
1202
1203
1204 type = new JClass("Object");
1205 } else {
1206 type = new JClass(baseType);
1207 }
1208
1209 boolean useJava5 = hasJavaSourceSupport(5);
1210 if (modelField.isArray()) {
1211 type = new JArrayType(type, useJava5);
1212 }
1213
1214 JField field = new JField(type, modelField.getName());
1215
1216 if (modelField.isModelVersionField()) {
1217 field.setInitString("\"" + getGeneratedVersion() + "\"");
1218 }
1219
1220 if (modelField.getDefaultValue() != null) {
1221 field.setInitString(getJavaDefaultValue(modelField));
1222 }
1223
1224 if (StringUtils.isNotEmpty(modelField.getDescription())) {
1225 field.setComment(appendPeriod(modelField.getDescription()));
1226 }
1227
1228 if (useJava5 && !modelField.getAnnotations().isEmpty()) {
1229 for (String annotation : modelField.getAnnotations()) {
1230 field.appendAnnotation(annotation);
1231 }
1232 }
1233
1234 return field;
1235 }
1236
1237 private void createField(JClass jClass, ModelField modelField) throws ModelloException {
1238 JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) modelField.getMetadata(JavaFieldMetadata.ID);
1239
1240 JField field = createField(modelField);
1241
1242 jClass.addField(field);
1243
1244 if (javaFieldMetadata.isGetter()) {
1245 jClass.addMethod(createGetter(field, modelField));
1246 }
1247
1248 if (javaFieldMetadata.isSetter()) {
1249 jClass.addMethod(createSetter(field, modelField));
1250 }
1251 }
1252
1253 private JMethod createGetter(JField field, ModelField modelField) {
1254 String propertyName = capitalise(field.getName());
1255
1256 JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) modelField.getMetadata(JavaFieldMetadata.ID);
1257
1258 String prefix = javaFieldMetadata.isBooleanGetter() ? "is" : "get";
1259
1260 JType returnType = field.getType();
1261 String interfaceCast = "";
1262
1263 if (modelField instanceof ModelAssociation) {
1264 ModelAssociation modelAssociation = (ModelAssociation) modelField;
1265
1266 JavaAssociationMetadata javaAssociationMetadata =
1267 (JavaAssociationMetadata) modelAssociation.getAssociationMetadata(JavaAssociationMetadata.ID);
1268
1269 if (StringUtils.isNotEmpty(javaAssociationMetadata.getInterfaceName())
1270 && !javaFieldMetadata.isBooleanGetter()) {
1271 returnType = new JClass(javaAssociationMetadata.getInterfaceName());
1272
1273 interfaceCast = "(" + javaAssociationMetadata.getInterfaceName() + ") ";
1274 }
1275 }
1276
1277 JMethod getter = new JMethod(prefix + propertyName, returnType, null);
1278
1279 StringBuilder comment = new StringBuilder("Get ");
1280 if (StringUtils.isEmpty(modelField.getDescription())) {
1281 comment.append("the ");
1282 comment.append(field.getName());
1283 comment.append(" field");
1284 } else {
1285 comment.append(
1286 StringUtils.lowercaseFirstLetter(modelField.getDescription().trim()));
1287 }
1288 getter.getJDocComment().setComment(appendPeriod(comment.toString()));
1289
1290 getter.getSourceCode().add("return " + interfaceCast + "this." + field.getName() + ";");
1291
1292 return getter;
1293 }
1294
1295 private JMethod createSetter(JField field, ModelField modelField) throws ModelloException {
1296 return createSetter(field, modelField, false);
1297 }
1298
1299
1300 private JMethod createSetter(JField field, ModelField modelField, boolean isBuilderMethod) throws ModelloException {
1301 String propertyName = capitalise(field.getName());
1302
1303 JMethod setter;
1304 if (isBuilderMethod) {
1305 setter = new JMethod("set" + propertyName, new JClass("Builder"), "this builder instance");
1306 } else {
1307 setter = new JMethod("set" + propertyName);
1308 }
1309
1310 StringBuilder comment = new StringBuilder("Set ");
1311 if (StringUtils.isEmpty(modelField.getDescription())) {
1312 comment.append("the ");
1313 comment.append(field.getName());
1314 comment.append(" field");
1315 } else {
1316 comment.append(
1317 StringUtils.lowercaseFirstLetter(modelField.getDescription().trim()));
1318 }
1319 setter.getJDocComment().setComment(appendPeriod(comment.toString()));
1320
1321 JType parameterType = getDesiredType(modelField, false);
1322
1323 setter.addParameter(new JParameter(parameterType, field.getName()));
1324
1325 JSourceCode sc = setter.getSourceCode();
1326
1327 if (modelField instanceof ModelAssociation) {
1328 ModelAssociation modelAssociation = (ModelAssociation) modelField;
1329
1330 JavaAssociationMetadata javaAssociationMetadata =
1331 (JavaAssociationMetadata) modelAssociation.getAssociationMetadata(JavaAssociationMetadata.ID);
1332
1333 boolean isOneMultiplicity =
1334 isBidirectionalAssociation(modelAssociation) && modelAssociation.isOneMultiplicity();
1335
1336 if (isOneMultiplicity && javaAssociationMetadata.isBidi()) {
1337 sc.add("if ( this." + field.getName() + " != null )");
1338
1339 sc.add("{");
1340
1341 sc.indent();
1342
1343 sc.add("this." + field.getName() + ".break"
1344 + modelAssociation.getModelClass().getName() + "Association( this );");
1345
1346 sc.unindent();
1347
1348 sc.add("}");
1349
1350 sc.add("");
1351 }
1352
1353 String interfaceCast = "";
1354
1355 if (StringUtils.isNotEmpty(javaAssociationMetadata.getInterfaceName())
1356 && modelAssociation.isOneMultiplicity()) {
1357 interfaceCast = "(" + field.getType().getName() + ") ";
1358
1359 createClassCastAssertion(sc, modelAssociation, "set");
1360 }
1361
1362 sc.add("this." + field.getName() + " = " + interfaceCast + field.getName() + ";");
1363
1364 if (isOneMultiplicity && javaAssociationMetadata.isBidi()) {
1365 sc.add("");
1366
1367 sc.add("if ( " + field.getName() + " != null )");
1368
1369 sc.add("{");
1370
1371 sc.indent();
1372
1373 sc.add("this." + field.getName() + ".create"
1374 + modelAssociation.getModelClass().getName() + "Association( this );");
1375
1376 sc.unindent();
1377
1378 sc.add("}");
1379 }
1380 } else {
1381 sc.add("this." + field.getName() + " = " + field.getName() + ";");
1382 }
1383
1384 if (isBuilderMethod) {
1385 sc.add("return this;");
1386 }
1387
1388 return setter;
1389 }
1390
1391 private void createClassCastAssertion(JSourceCode sc, ModelAssociation modelAssociation, String crudModifier)
1392 throws ModelloException {
1393 JavaAssociationMetadata javaAssociationMetadata =
1394 (JavaAssociationMetadata) modelAssociation.getAssociationMetadata(JavaAssociationMetadata.ID);
1395
1396 if (StringUtils.isEmpty(javaAssociationMetadata.getInterfaceName())) {
1397 return;
1398 }
1399
1400 String propertyName = capitalise(modelAssociation.getName());
1401
1402 JField field = createField(modelAssociation);
1403 String fieldName = field.getName();
1404 JType type = new JClass(modelAssociation.getTo());
1405
1406 if (modelAssociation.isManyMultiplicity()) {
1407 fieldName = uncapitalise(modelAssociation.getTo());
1408 }
1409
1410 String instanceName = type.getName();
1411
1412
1413
1414
1415 sc.add("if ( " + fieldName + " != null && !( " + fieldName + " instanceof " + instanceName + " ) )");
1416
1417 sc.add("{");
1418
1419 sc.indent();
1420
1421 sc.add("throw new ClassCastException( \""
1422 + modelAssociation.getModelClass().getName() + "." + crudModifier
1423 + propertyName + "( " + fieldName + " ) parameter must be instanceof \" + " + instanceName
1424 + ".class.getName() );");
1425
1426 sc.unindent();
1427
1428 sc.add("}");
1429 }
1430
1431 private void createAssociation(JClass jClass, ModelAssociation modelAssociation, JSourceCode jConstructorSource)
1432 throws ModelloException {
1433 JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) modelAssociation.getMetadata(JavaFieldMetadata.ID);
1434
1435 JavaAssociationMetadata javaAssociationMetadata = getJavaAssociationMetadata(modelAssociation);
1436
1437 boolean useJava5 = hasJavaSourceSupport(5);
1438
1439 if (modelAssociation.isManyMultiplicity()) {
1440 JType componentType = getComponentType(modelAssociation, javaAssociationMetadata);
1441
1442 String defaultValue = getDefaultValue(modelAssociation, componentType);
1443
1444 JType type;
1445 if (modelAssociation.isGenericType()) {
1446 type = new JCollectionType(modelAssociation.getType(), componentType, useJava5);
1447 } else if (ModelDefault.MAP.equals(modelAssociation.getType())) {
1448 JMapType mapType = new JMapType(modelAssociation.getType(), defaultValue, componentType, useJava5);
1449 defaultValue = mapType.getInstanceName();
1450 type = mapType;
1451 } else {
1452 type = new JClass(modelAssociation.getType());
1453 }
1454
1455 JField jField = new JField(type, modelAssociation.getName());
1456
1457 if (!isEmpty(modelAssociation.getComment())) {
1458 jField.setComment(modelAssociation.getComment());
1459 }
1460
1461 if (useJava5 && !modelAssociation.getAnnotations().isEmpty()) {
1462 for (String annotation : modelAssociation.getAnnotations()) {
1463 jField.appendAnnotation(annotation);
1464 }
1465 }
1466
1467 if (Objects.equals(javaAssociationMetadata.getInitializationMode(), JavaAssociationMetadata.FIELD_INIT)) {
1468 jField.setInitString(defaultValue);
1469 }
1470
1471 if (Objects.equals(
1472 javaAssociationMetadata.getInitializationMode(), JavaAssociationMetadata.CONSTRUCTOR_INIT)) {
1473 jConstructorSource.add("this." + jField.getName() + " = " + defaultValue + ";");
1474 }
1475
1476 jClass.addField(jField);
1477
1478 if (javaFieldMetadata.isGetter()) {
1479 String propertyName = capitalise(jField.getName());
1480
1481 JMethod getter = new JMethod("get" + propertyName, jField.getType(), null);
1482
1483 JSourceCode sc = getter.getSourceCode();
1484
1485 if (Objects.equals(
1486 javaAssociationMetadata.getInitializationMode(), JavaAssociationMetadata.LAZY_INIT)) {
1487 sc.add("if ( this." + jField.getName() + " == null )");
1488
1489 sc.add("{");
1490
1491 sc.indent();
1492
1493 sc.add("this." + jField.getName() + " = " + defaultValue + ";");
1494
1495 sc.unindent();
1496
1497 sc.add("}");
1498
1499 sc.add("");
1500 }
1501
1502 sc.add("return this." + jField.getName() + ";");
1503
1504 jClass.addMethod(getter);
1505 }
1506
1507 if (javaFieldMetadata.isSetter()) {
1508 jClass.addMethod(createSetter(jField, modelAssociation));
1509 }
1510
1511 if (javaAssociationMetadata.isAdder()) {
1512 createAdder(modelAssociation, jClass);
1513 }
1514 } else {
1515 createField(jClass, modelAssociation);
1516 }
1517
1518 if (isBidirectionalAssociation(modelAssociation)) {
1519 if (javaAssociationMetadata.isBidi()) {
1520 createCreateAssociation(jClass, modelAssociation);
1521 createBreakAssociation(jClass, modelAssociation);
1522 }
1523 }
1524 }
1525
1526 private String getDefaultValue(ModelAssociation modelAssociation, JType componentType) {
1527 String defaultValue = getDefaultValue(modelAssociation);
1528
1529 if (modelAssociation.isGenericType()) {
1530 ModelDefault modelDefault = getModel().getDefault(modelAssociation.getType());
1531
1532 if (hasJavaSourceSupport(5)) {
1533 defaultValue = StringUtils.replace(modelDefault.getValue(), "<?>", "<" + componentType.getName() + ">");
1534 } else {
1535 defaultValue =
1536 StringUtils.replace(modelDefault.getValue(), "<?>", "/*<" + componentType.getName() + ">*/");
1537 }
1538 }
1539 return defaultValue;
1540 }
1541
1542 private JavaAssociationMetadata getJavaAssociationMetadata(ModelAssociation modelAssociation)
1543 throws ModelloException {
1544 JavaAssociationMetadata javaAssociationMetadata =
1545 (JavaAssociationMetadata) modelAssociation.getAssociationMetadata(JavaAssociationMetadata.ID);
1546
1547 if (!JavaAssociationMetadata.INIT_TYPES.contains(javaAssociationMetadata.getInitializationMode())) {
1548 throw new ModelloException("The Java Modello Generator cannot use '"
1549 + javaAssociationMetadata.getInitializationMode()
1550 + "' as a <association java.init=\"" + javaAssociationMetadata.getInitializationMode() + "\"> "
1551 + "value, the only the following are acceptable " + JavaAssociationMetadata.INIT_TYPES);
1552 }
1553 return javaAssociationMetadata;
1554 }
1555
1556 private JType getComponentType(ModelAssociation modelAssociation, JavaAssociationMetadata javaAssociationMetadata) {
1557 JType componentType;
1558 if (javaAssociationMetadata.getInterfaceName() != null) {
1559 componentType = new JClass(javaAssociationMetadata.getInterfaceName());
1560 } else {
1561 componentType = new JClass(modelAssociation.getTo());
1562 }
1563 return componentType;
1564 }
1565
1566 private void createCreateAssociation(JClass jClass, ModelAssociation modelAssociation) {
1567 JMethod createMethod = new JMethod("create" + modelAssociation.getTo() + "Association");
1568
1569 JavaAssociationMetadata javaAssociationMetadata =
1570 (JavaAssociationMetadata) modelAssociation.getAssociationMetadata(JavaAssociationMetadata.ID);
1571
1572 createMethod.addParameter(
1573 new JParameter(new JClass(modelAssociation.getTo()), uncapitalise(modelAssociation.getTo())));
1574
1575
1576
1577
1578 JSourceCode sc = createMethod.getSourceCode();
1579
1580 if (modelAssociation.isOneMultiplicity()) {
1581 if (javaAssociationMetadata.isBidi()) {
1582 sc.add("if ( this." + modelAssociation.getName() + " != null )");
1583
1584 sc.add("{");
1585
1586 sc.indent();
1587
1588 sc.add("break" + modelAssociation.getTo() + "Association( this." + modelAssociation.getName() + " );");
1589
1590 sc.unindent();
1591
1592 sc.add("}");
1593
1594 sc.add("");
1595 }
1596
1597 sc.add("this." + modelAssociation.getName() + " = " + uncapitalise(modelAssociation.getTo()) + ";");
1598 } else {
1599 jClass.addImport("java.util.Collection");
1600
1601 sc.add("Collection " + modelAssociation.getName() + " = get" + capitalise(modelAssociation.getName())
1602 + "();");
1603
1604 sc.add("");
1605
1606 sc.add("if ( " + modelAssociation.getName() + ".contains( " + uncapitalise(modelAssociation.getTo())
1607 + " ) )");
1608
1609 sc.add("{");
1610
1611 sc.indent();
1612
1613 sc.add("throw new IllegalStateException( \"" + uncapitalise(modelAssociation.getTo())
1614 + " is already assigned.\" );");
1615
1616 sc.unindent();
1617
1618 sc.add("}");
1619
1620 sc.add("");
1621
1622 sc.add(modelAssociation.getName() + ".add( " + uncapitalise(modelAssociation.getTo()) + " );");
1623 }
1624
1625 jClass.addMethod(createMethod);
1626 }
1627
1628 private void createBreakAssociation(JClass jClass, ModelAssociation modelAssociation) {
1629 JSourceCode sc;
1630 JMethod breakMethod = new JMethod("break" + modelAssociation.getTo() + "Association");
1631
1632 breakMethod.addParameter(
1633 new JParameter(new JClass(modelAssociation.getTo()), uncapitalise(modelAssociation.getTo())));
1634
1635
1636
1637
1638 sc = breakMethod.getSourceCode();
1639
1640 if (modelAssociation.isOneMultiplicity()) {
1641 sc.add("if ( this." + modelAssociation.getName() + " != " + uncapitalise(modelAssociation.getTo()) + " )");
1642
1643 sc.add("{");
1644
1645 sc.indent();
1646
1647 sc.add("throw new IllegalStateException( \"" + uncapitalise(modelAssociation.getTo())
1648 + " isn't associated.\" );");
1649
1650 sc.unindent();
1651
1652 sc.add("}");
1653
1654 sc.add("");
1655
1656 sc.add("this." + modelAssociation.getName() + " = null;");
1657 } else {
1658 JavaAssociationMetadata javaAssociationMetadata =
1659 (JavaAssociationMetadata) modelAssociation.getAssociationMetadata(JavaAssociationMetadata.ID);
1660
1661 String reference;
1662
1663 if (JavaAssociationMetadata.LAZY_INIT.equals(javaAssociationMetadata.getInitializationMode())) {
1664 reference = "get" + capitalise(modelAssociation.getName()) + "()";
1665 } else {
1666 reference = modelAssociation.getName();
1667 }
1668
1669 sc.add("if ( !" + reference + ".contains( " + uncapitalise(modelAssociation.getTo()) + " ) )");
1670
1671 sc.add("{");
1672
1673 sc.indent();
1674
1675 sc.add("throw new IllegalStateException( \"" + uncapitalise(modelAssociation.getTo())
1676 + " isn't associated.\" );");
1677
1678 sc.unindent();
1679
1680 sc.add("}");
1681
1682 sc.add("");
1683
1684 sc.add(reference + ".remove( " + uncapitalise(modelAssociation.getTo()) + " );");
1685 }
1686
1687 jClass.addMethod(breakMethod);
1688 }
1689
1690 private void createAdder(ModelAssociation modelAssociation, JClass jClass) throws ModelloException {
1691 createAdder(modelAssociation, jClass, false);
1692 }
1693
1694
1695
1696
1697 private void createAdder(ModelAssociation modelAssociation, JClass jClass, boolean isBuilderMethod)
1698 throws ModelloException {
1699 String fieldName = modelAssociation.getName();
1700
1701 JavaAssociationMetadata javaAssociationMetadata =
1702 (JavaAssociationMetadata) modelAssociation.getAssociationMetadata(JavaAssociationMetadata.ID);
1703
1704 String parameterName = uncapitalise(modelAssociation.getTo());
1705 String implementationParameterName = parameterName;
1706
1707 boolean bidirectionalAssociation = isBidirectionalAssociation(modelAssociation);
1708
1709 JType addType;
1710
1711 if (StringUtils.isNotEmpty(javaAssociationMetadata.getInterfaceName())) {
1712 addType = new JClass(javaAssociationMetadata.getInterfaceName());
1713 implementationParameterName = "( (" + modelAssociation.getTo() + ") " + parameterName + " )";
1714 } else if (modelAssociation.getToClass() != null) {
1715 addType = new JClass(modelAssociation.getToClass().getName());
1716 } else {
1717 addType = new JClass("String");
1718 }
1719
1720 if (modelAssociation.getType().equals(ModelDefault.PROPERTIES)
1721 || modelAssociation.getType().equals(ModelDefault.MAP)) {
1722 String adderName = "add" + capitalise(singular(fieldName));
1723
1724 JMethod adder;
1725 if (isBuilderMethod) {
1726 adder = new JMethod(adderName, new JClass("Builder"), "this builder instance");
1727 } else {
1728 adder = new JMethod(adderName);
1729 }
1730
1731 if (modelAssociation.getType().equals(ModelDefault.MAP)) {
1732 adder.addParameter(new JParameter(new JClass("Object"), "key"));
1733 } else {
1734 adder.addParameter(new JParameter(new JClass("String"), "key"));
1735 }
1736
1737 adder.addParameter(new JParameter(new JClass(modelAssociation.getTo()), "value"));
1738
1739 StringBuilder adderCode = new StringBuilder();
1740
1741 if (JavaAssociationMetadata.LAZY_INIT.equals(javaAssociationMetadata.getInitializationMode())
1742 && !isBuilderMethod) {
1743 adderCode.append("get").append(capitalise(fieldName)).append("()");
1744 } else {
1745 adderCode.append(fieldName);
1746 }
1747
1748 adderCode.append(".put( key, value );");
1749
1750 adder.getSourceCode().add(adderCode.toString());
1751
1752 if (isBuilderMethod) {
1753 adder.getSourceCode().add("return this;");
1754 }
1755
1756 jClass.addMethod(adder);
1757 } else {
1758 String adderName = "add" + singular(capitalise(singular(fieldName)));
1759
1760 JMethod adder;
1761 if (isBuilderMethod) {
1762 adder = new JMethod(adderName, new JClass("Builder"), "this builder instance");
1763 } else {
1764 adder = new JMethod(adderName);
1765 }
1766
1767 adder.addParameter(new JParameter(addType, parameterName));
1768
1769 createClassCastAssertion(adder.getSourceCode(), modelAssociation, "add");
1770
1771 StringBuilder adderCode = new StringBuilder();
1772
1773 if (JavaAssociationMetadata.LAZY_INIT.equals(javaAssociationMetadata.getInitializationMode())
1774 && !isBuilderMethod) {
1775 adderCode.append("get").append(capitalise(fieldName)).append("()");
1776 } else {
1777 adderCode.append(fieldName);
1778 }
1779
1780 adderCode.append(".add( ").append(implementationParameterName).append(" );");
1781
1782 adder.getSourceCode().add(adderCode.toString());
1783
1784 if (bidirectionalAssociation && javaAssociationMetadata.isBidi() && !isBuilderMethod) {
1785
1786
1787
1788 adder.getSourceCode()
1789 .add(implementationParameterName + ".create"
1790 + modelAssociation.getModelClass().getName() + "Association( this );");
1791 }
1792
1793 if (isBuilderMethod) {
1794 adder.getSourceCode().add("return this;");
1795 }
1796
1797 jClass.addMethod(adder);
1798
1799
1800 if (isBuilderMethod) {
1801 return;
1802 }
1803
1804 JMethod remover = new JMethod("remove" + singular(capitalise(fieldName)));
1805
1806 remover.addParameter(new JParameter(addType, parameterName));
1807
1808 createClassCastAssertion(remover.getSourceCode(), modelAssociation, "remove");
1809
1810 if (bidirectionalAssociation && javaAssociationMetadata.isBidi()) {
1811
1812
1813
1814 remover.getSourceCode()
1815 .add(parameterName + ".break"
1816 + modelAssociation.getModelClass().getName() + "Association( this );");
1817 }
1818
1819 String reference;
1820
1821 if (JavaAssociationMetadata.LAZY_INIT.equals(javaAssociationMetadata.getInitializationMode())) {
1822 reference = "get" + capitalise(fieldName) + "()";
1823 } else {
1824 reference = fieldName;
1825 }
1826
1827 remover.getSourceCode().add(reference + ".remove( " + implementationParameterName + " );");
1828
1829 jClass.addMethod(remover);
1830 }
1831 }
1832
1833 private boolean isBidirectionalAssociation(ModelAssociation association) {
1834 Model model = association.getModelClass().getModel();
1835
1836 if (!isClassInModel(association.getTo(), model)) {
1837 return false;
1838 }
1839
1840 ModelClass toClass = association.getToClass();
1841
1842 for (ModelField modelField : toClass.getFields(getGeneratedVersion())) {
1843 if (!(modelField instanceof ModelAssociation)) {
1844 continue;
1845 }
1846
1847 ModelAssociation modelAssociation = (ModelAssociation) modelField;
1848
1849 if (association == modelAssociation) {
1850 continue;
1851 }
1852
1853 if (!isClassInModel(modelAssociation.getTo(), model)) {
1854 continue;
1855 }
1856
1857 ModelClass totoClass = modelAssociation.getToClass();
1858
1859 if (association.getModelClass().equals(totoClass)) {
1860 return true;
1861 }
1862 }
1863
1864 return false;
1865 }
1866
1867 private JType getDesiredType(ModelField modelField, boolean useTo) throws ModelloException {
1868 JField field = createField(modelField);
1869 JType type = field.getType();
1870
1871 if (modelField instanceof ModelAssociation) {
1872 ModelAssociation modelAssociation = (ModelAssociation) modelField;
1873 JavaAssociationMetadata javaAssociationMetadata =
1874 (JavaAssociationMetadata) modelAssociation.getAssociationMetadata(JavaAssociationMetadata.ID);
1875
1876 if (StringUtils.isNotEmpty(javaAssociationMetadata.getInterfaceName())
1877 && !modelAssociation.isManyMultiplicity()) {
1878 type = new JClass(javaAssociationMetadata.getInterfaceName());
1879 } else if (modelAssociation.isManyMultiplicity() && modelAssociation.isGenericType()) {
1880 JType componentType = getComponentType(modelAssociation, javaAssociationMetadata);
1881 type = new JCollectionType(modelAssociation.getType(), componentType, hasJavaSourceSupport(5));
1882 } else if (useTo) {
1883 type = new JClass(modelAssociation.getTo());
1884 }
1885 }
1886
1887 return type;
1888 }
1889
1890 private void addParameter(JMethodSignature jMethod, String type, String name, String comment) {
1891 jMethod.addParameter(new JParameter(new JType(type), name));
1892 jMethod.getJDocComment().getParamDescriptor(name).setDescription(comment);
1893 }
1894
1895
1896
1897
1898
1899
1900 private void generateBuilder(ModelClass modelClass, JClass builderClass, JConstructor outherClassConstructor)
1901 throws ModelloException {
1902 builderClass.getModifiers().setStatic(true);
1903 builderClass.getModifiers().setFinal(true);
1904
1905 ModelClass reference = modelClass;
1906
1907
1908 while (reference != null) {
1909
1910 for (ModelField modelField : reference.getFields(getGeneratedVersion())) {
1911 if (modelField instanceof ModelAssociation) {
1912 createBuilderAssociation(builderClass, (ModelAssociation) modelField);
1913 } else {
1914 createBuilderField(builderClass, modelField);
1915 }
1916 }
1917
1918 if (reference.hasSuperClass()) {
1919 reference = reference.getModel().getClass(reference.getSuperClass(), getGeneratedVersion());
1920 } else {
1921 reference = null;
1922 }
1923 }
1924
1925
1926 JMethod build = new JMethod(
1927 "build", new JClass(modelClass.getName()), "A new <code>" + modelClass.getName() + "</code> instance");
1928 build.getJDocComment().setComment("Creates a new <code>" + modelClass.getName() + "</code> instance.");
1929
1930 JSourceCode sc = build.getSourceCode();
1931
1932 createInstanceAndSetProperties(modelClass, outherClassConstructor, sc);
1933
1934 builderClass.addMethod(build);
1935 }
1936
1937 private void createInstanceAndSetProperties(ModelClass modelClass, JConstructor constructor, JSourceCode sc)
1938 throws ModelloException {
1939 final Set<String> ctorArgs = new HashSet<String>();
1940
1941 StringBuilder ctor = new StringBuilder(modelClass.getName())
1942 .append(" instance = new ")
1943 .append(modelClass.getName())
1944 .append('(');
1945
1946
1947 if (constructor != null) {
1948 JParameter[] parameters = constructor.getParameters();
1949 for (int i = 0; i < parameters.length; i++) {
1950 if (i > 0) {
1951 ctor.append(',');
1952 }
1953
1954 JParameter parameter = parameters[i];
1955
1956 ctor.append(' ').append(parameter.getName()).append(' ');
1957
1958 ctorArgs.add(parameter.getName());
1959 }
1960 }
1961
1962 ctor.append(");");
1963
1964 sc.add(ctor.toString());
1965
1966 ModelClass reference = modelClass;
1967
1968
1969 while (reference != null) {
1970
1971 for (ModelField modelField : reference.getFields(getGeneratedVersion())) {
1972 if (modelField instanceof ModelAssociation) {
1973 ModelAssociation modelAssociation = (ModelAssociation) modelField;
1974 JavaFieldMetadata javaFieldMetadata =
1975 (JavaFieldMetadata) modelField.getMetadata(JavaFieldMetadata.ID);
1976 JavaAssociationMetadata javaAssociationMetadata = getJavaAssociationMetadata(modelAssociation);
1977
1978 if (modelAssociation.isManyMultiplicity()
1979 && !javaFieldMetadata.isGetter()
1980 && !javaFieldMetadata.isSetter()
1981 && !javaAssociationMetadata.isAdder()) {
1982 throw new ModelloException(
1983 "Exception while generating Java, Model inconsistency found: impossible to generate '"
1984 + modelClass.getName()
1985 + ".Builder#build()' method, '"
1986 + modelClass.getName()
1987 + "."
1988 + modelAssociation.getName()
1989 + "' field ("
1990 + modelAssociation.getType()
1991 + ") cannot be set, no getter/setter/adder method available.");
1992 }
1993
1994 createSetBuilderAssociationToInstance(ctorArgs, modelAssociation, sc);
1995 } else {
1996 createSetBuilderFieldToInstance(ctorArgs, modelField, sc);
1997 }
1998 }
1999
2000 if (reference.hasSuperClass()) {
2001 reference = reference.getModel().getClass(reference.getSuperClass(), getGeneratedVersion());
2002 } else {
2003 reference = null;
2004 }
2005 }
2006
2007 sc.add("return instance;");
2008 }
2009
2010 private void createBuilderField(JClass jClass, ModelField modelField) throws ModelloException {
2011 JField field = createField(modelField);
2012
2013 jClass.addField(field);
2014
2015 jClass.addMethod(createSetter(field, modelField, true));
2016 }
2017
2018 private boolean createSetBuilderFieldToInstance(Set<String> ctorArgs, ModelField modelField, JSourceCode sc)
2019 throws ModelloException {
2020 JavaFieldMetadata javaFieldMetadata = (JavaFieldMetadata) modelField.getMetadata(JavaFieldMetadata.ID);
2021
2022
2023 if (!ctorArgs.contains(modelField.getName()) && javaFieldMetadata.isSetter()) {
2024 sc.add("instance.set" + capitalise(modelField.getName()) + "( " + modelField.getName() + " );");
2025 return true;
2026 }
2027
2028 return false;
2029 }
2030
2031 private void createBuilderAssociation(JClass jClass, ModelAssociation modelAssociation) throws ModelloException {
2032 JavaAssociationMetadata javaAssociationMetadata = getJavaAssociationMetadata(modelAssociation);
2033
2034 if (modelAssociation.isManyMultiplicity()) {
2035 JType componentType = getComponentType(modelAssociation, javaAssociationMetadata);
2036 boolean useJava5 = hasJavaSourceSupport(5);
2037
2038 String defaultValue = getDefaultValue(modelAssociation, componentType);
2039
2040 JType type;
2041 if (modelAssociation.isGenericType()) {
2042 type = new JCollectionType(modelAssociation.getType(), componentType, useJava5);
2043 } else if (ModelDefault.MAP.equals(modelAssociation.getType())) {
2044 JMapType mapType = new JMapType(modelAssociation.getType(), defaultValue, componentType, useJava5);
2045 defaultValue = mapType.getInstanceName();
2046 type = mapType;
2047 } else {
2048 type = new JClass(modelAssociation.getType());
2049 }
2050
2051 JField jField = new JField(type, modelAssociation.getName());
2052 jField.getModifiers().setFinal(true);
2053
2054 if (!isEmpty(modelAssociation.getComment())) {
2055 jField.setComment(modelAssociation.getComment());
2056 }
2057
2058 if (useJava5 && !modelAssociation.getAnnotations().isEmpty()) {
2059 for (String annotation : modelAssociation.getAnnotations()) {
2060 jField.appendAnnotation(annotation);
2061 }
2062 }
2063
2064 jField.setInitString(defaultValue);
2065
2066 jClass.addField(jField);
2067
2068 createAdder(modelAssociation, jClass, true);
2069 } else {
2070 createBuilderField(jClass, modelAssociation);
2071 }
2072 }
2073
2074 private void createSetBuilderAssociationToInstance(
2075 Set<String> ctorArgs, ModelAssociation modelAssociation, JSourceCode sc) throws ModelloException {
2076 if (modelAssociation.isManyMultiplicity()) {
2077
2078 if (modelAssociation.getType().equals(ModelDefault.PROPERTIES)
2079 || modelAssociation.getType().equals(ModelDefault.MAP)) {
2080 if (createSetBuilderFieldToInstance(ctorArgs, modelAssociation, sc)) {
2081 return;
2082 }
2083 }
2084
2085
2086
2087 JavaAssociationMetadata javaAssociationMetadata = getJavaAssociationMetadata(modelAssociation);
2088
2089 boolean bidirectionalAssociation = isBidirectionalAssociation(modelAssociation);
2090
2091 if (!bidirectionalAssociation || !javaAssociationMetadata.isBidi()) {
2092 JavaFieldMetadata javaFieldMetadata =
2093 (JavaFieldMetadata) modelAssociation.getMetadata(JavaFieldMetadata.ID);
2094
2095
2096 if (createSetBuilderFieldToInstance(ctorArgs, modelAssociation, sc)) {
2097 return;
2098 }
2099
2100 else if (javaFieldMetadata.isGetter()) {
2101 String action = isMap(modelAssociation.getType()) ? "put" : "add";
2102
2103 sc.add("instance.get" + capitalise(modelAssociation.getName()) + "()." + action + "All( "
2104 + modelAssociation.getName() + " );");
2105 return;
2106 }
2107 }
2108
2109
2110
2111
2112
2113
2114 String itemType;
2115 String targetField = modelAssociation.getName();
2116
2117 boolean useJava5 = hasJavaSourceSupport(5);
2118
2119 if (StringUtils.isNotEmpty(javaAssociationMetadata.getInterfaceName())) {
2120 itemType = javaAssociationMetadata.getInterfaceName();
2121 } else if (modelAssociation.getToClass() != null) {
2122 itemType = modelAssociation.getToClass().getName();
2123 } else if (modelAssociation.getType().equals(ModelDefault.PROPERTIES)
2124 || modelAssociation.getType().equals(ModelDefault.MAP)) {
2125 StringBuilder itemTypeBuilder = new StringBuilder("java.util.Map.Entry");
2126
2127 if (useJava5) {
2128 itemTypeBuilder.append('<');
2129
2130 if (modelAssociation.getType().equals(ModelDefault.PROPERTIES)) {
2131 itemTypeBuilder.append("Object, Object");
2132 } else {
2133 itemTypeBuilder.append("String, ").append(modelAssociation.getTo());
2134 }
2135
2136 itemTypeBuilder.append('>');
2137 }
2138
2139 itemType = itemTypeBuilder.toString();
2140
2141 targetField += ".entrySet()";
2142 } else {
2143 itemType = "String";
2144 }
2145
2146 if (useJava5) {
2147 sc.add("for ( " + itemType + " item : " + targetField + " )");
2148 } else {
2149 sc.add("for ( java.util.Iterator it = " + targetField + ".iterator(); it.hasNext(); )");
2150 }
2151
2152 sc.add("{");
2153 sc.indent();
2154
2155 if (!useJava5) {
2156 sc.add(itemType + " item = (" + itemType + ") it.next();");
2157 }
2158
2159 StringBuilder adder = new StringBuilder("instance.add")
2160 .append(capitalise(singular(modelAssociation.getName())))
2161 .append("( ");
2162
2163 if (modelAssociation.getType().equals(ModelDefault.PROPERTIES)
2164 || modelAssociation.getType().equals(ModelDefault.MAP)) {
2165 appendEntryMethod("String", "getKey()", adder, modelAssociation);
2166 adder.append(", ");
2167 appendEntryMethod(modelAssociation.getTo(), "getValue()", adder, modelAssociation);
2168 } else {
2169 adder.append("item");
2170 }
2171
2172 adder.append(" );");
2173
2174 sc.add(adder.toString());
2175
2176 sc.unindent();
2177 sc.add("}");
2178 } else {
2179 createSetBuilderFieldToInstance(ctorArgs, modelAssociation, sc);
2180 }
2181 }
2182
2183 private void appendEntryMethod(
2184 String type, String method, StringBuilder target, ModelAssociation modelAssociation) {
2185 if (!hasJavaSourceSupport(5) || modelAssociation.getType().equals(ModelDefault.PROPERTIES)) {
2186 target.append('(').append(type).append(") ");
2187 }
2188
2189 target.append("item.").append(method);
2190 }
2191
2192 private void generateStaticCreator(ModelClass modelClass, JClass jClass, JConstructor constructor)
2193 throws ModelloException {
2194 JMethod creatorMethod = new JMethod(
2195 "new" + modelClass.getName() + "Instance",
2196 new JClass(modelClass.getName()),
2197 "a new <code>" + modelClass.getName() + "</code> instance.");
2198 creatorMethod.getModifiers().setStatic(true);
2199 creatorMethod.getJDocComment().setComment("Creates a new <code>" + modelClass.getName() + "</code> instance.");
2200
2201 ModelClass reference = modelClass;
2202
2203 boolean hasDefaults = false;
2204
2205
2206 while (reference != null) {
2207 for (ModelField modelField : reference.getFields(getGeneratedVersion())) {
2208
2209 JField field = createField(modelField);
2210 creatorMethod.addParameter(new JParameter(field.getType(), field.getName()));
2211
2212 if (!StringUtils.isEmpty(modelField.getDefaultValue())) {
2213 hasDefaults = true;
2214 }
2215 }
2216
2217 if (reference.hasSuperClass()) {
2218 reference = reference.getModel().getClass(reference.getSuperClass(), getGeneratedVersion());
2219 } else {
2220 reference = null;
2221 }
2222 }
2223
2224 JSourceCode sc = creatorMethod.getSourceCode();
2225
2226 createInstanceAndSetProperties(modelClass, constructor, sc);
2227
2228 jClass.addMethod(creatorMethod);
2229
2230
2231 if (!hasDefaults) {
2232 return;
2233 }
2234
2235 creatorMethod = new JMethod(
2236 "new" + modelClass.getName() + "Instance",
2237 new JClass(modelClass.getName()),
2238 "a new <code>" + modelClass.getName() + "</code> instance.");
2239 creatorMethod.getModifiers().setStatic(true);
2240 creatorMethod.getJDocComment().setComment("Creates a new <code>" + modelClass.getName() + "</code> instance.");
2241
2242 StringBuilder shortcutArgs = new StringBuilder();
2243
2244 reference = modelClass;
2245
2246 while (reference != null) {
2247 for (ModelField modelField : reference.getFields(getGeneratedVersion())) {
2248 if (shortcutArgs.length() > 0) {
2249 shortcutArgs.append(',');
2250 }
2251
2252 shortcutArgs.append(' ');
2253
2254 if (StringUtils.isEmpty(modelField.getDefaultValue())) {
2255
2256 JField field = createField(modelField);
2257 creatorMethod.addParameter(new JParameter(field.getType(), field.getName()));
2258
2259 shortcutArgs.append(modelField.getName());
2260 } else {
2261 shortcutArgs.append(getJavaDefaultValue(modelField));
2262 }
2263
2264 shortcutArgs.append(' ');
2265 }
2266
2267 if (reference.hasSuperClass()) {
2268 reference = reference.getModel().getClass(reference.getSuperClass(), getGeneratedVersion());
2269 } else {
2270 reference = null;
2271 }
2272 }
2273
2274 sc = creatorMethod.getSourceCode();
2275
2276 sc.add("return new" + modelClass.getName() + "Instance(" + shortcutArgs + ");");
2277
2278 jClass.addMethod(creatorMethod);
2279 }
2280 }