KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > netbeans > modules > j2ee > persistence > wizard > fromdb > JavaPersistenceGenerator


1 /*
2  * The contents of this file are subject to the terms of the Common Development
3  * and Distribution License (the License). You may not use this file except in
4  * compliance with the License.
5  *
6  * You can obtain a copy of the License at http://www.netbeans.org/cddl.html
7  * or http://www.netbeans.org/cddl.txt.
8  *
9  * When distributing Covered Code, include this CDDL Header Notice in each file
10  * and include the License file at http://www.netbeans.org/cddl.txt.
11  * If applicable, add the following below the CDDL Header, with the fields
12  * enclosed by brackets [] replaced by your own identifying information:
13  * "Portions Copyrighted [year] [name of copyright owner]"
14  *
15  * The Original Software is NetBeans. The Initial Developer of the Original
16  * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
17  * Microsystems, Inc. All Rights Reserved.
18  */

19
20 package org.netbeans.modules.j2ee.persistence.wizard.fromdb;
21
22 import com.sun.source.tree.*;
23 import java.util.HashMap JavaDoc;
24 import org.netbeans.api.progress.ProgressHandle;
25 import org.netbeans.modules.j2ee.persistence.api.metadata.orm.Entity;
26 import org.netbeans.modules.j2ee.persistence.api.metadata.orm.Table;
27 import org.openide.filesystems.FileObject;
28 import java.io.IOException JavaDoc;
29 import java.util.ArrayList JavaDoc;
30 import java.util.Collections JavaDoc;
31 import java.util.EnumSet JavaDoc;
32 import java.util.HashSet JavaDoc;
33 import java.util.Iterator JavaDoc;
34 import java.util.List JavaDoc;
35 import java.util.Map JavaDoc;
36 import java.util.Set JavaDoc;
37 import java.util.logging.Level JavaDoc;
38 import java.util.logging.Logger JavaDoc;
39 import javax.lang.model.element.*;
40 import javax.lang.model.type.*;
41 import org.netbeans.api.java.classpath.ClassPath;
42 import org.netbeans.api.java.source.JavaSource;
43 import org.netbeans.api.java.source.TreeMaker;
44 import org.netbeans.api.java.source.WorkingCopy;
45 import org.netbeans.api.project.FileOwnerQuery;
46 import org.netbeans.api.project.Project;
47 import org.netbeans.modules.j2ee.persistence.util.AbstractTask;
48 import org.netbeans.modules.j2ee.persistence.util.GenerationUtils;
49 import org.netbeans.modules.j2ee.persistence.dd.persistence.model_1_0.PersistenceUnit;
50 import org.netbeans.modules.j2ee.persistence.entitygenerator.CMPMappingModel;
51 import org.netbeans.modules.j2ee.persistence.entitygenerator.EntityClass;
52 import org.netbeans.modules.j2ee.persistence.entitygenerator.EntityMember;
53 import org.netbeans.modules.j2ee.persistence.entitygenerator.RelationshipRole;
54 import org.netbeans.modules.j2ee.persistence.provider.InvalidPersistenceXmlException;
55 import org.netbeans.modules.j2ee.persistence.provider.ProviderUtil;
56 import org.netbeans.modules.j2ee.persistence.unit.PUDataObject;
57 import org.netbeans.modules.j2ee.persistence.util.JPAClassPathHelper;
58 import org.netbeans.modules.j2ee.persistence.wizard.Util;
59 import org.openide.WizardDescriptor;
60 import org.openide.filesystems.FileUtil;
61 import org.openide.util.NbBundle;
62
63 /**
64  * Generator of Java Persistence API ORM classes from DB.
65  *
66  * @author Pavel Buzek, Andrei Badea
67  */

68 public class JavaPersistenceGenerator implements PersistenceGenerator {
69
70     // XXX Javadoc for generated code missing in many places - issue 90302
71
// XXX createToStringMethod() could be moved to GenerationUtils
72
// XXX init() commented out until annotation model is implemented
73

74     // XXX empty lines in generated hashCode() - issue 90186
75
// XXX comments are lost in method body passed as string - issue 89873
76
// XXX return 0, 1 in generated equals() - issue 90183
77
// XXX empty line in generated equals() - issue 90186
78

79     private final Map JavaDoc<String JavaDoc, String JavaDoc> entityName2TableName = new HashMap JavaDoc<String JavaDoc, String JavaDoc>();
80
81     // options (not currently exposed in UI)
82
// field vs. property access
83
private static boolean fieldAccess = true;
84     // named input params for named queries vs. positional params
85
private static boolean genNamedParams = true;
86     // should generated Entity Classes implement Serializable?
87
private static boolean genSerializableEntities = true;
88
89     public JavaPersistenceGenerator() {
90     }
91
92     public void generateBeans(final ProgressPanel progressPanel,
93             final RelatedCMPHelper helper,
94             final FileObject dbSchemaFile,
95             final ProgressHandle handle,
96             boolean justTesting) throws IOException JavaDoc {
97         new Generator(helper, handle, progressPanel).run();
98     }
99
100     public void init(WizardDescriptor wiz) {
101         // get the table names for all entities in the project
102
// Project project = Templates.getProject(wiz);
103
// try {
104
// processEntities(PersistenceUtils.getEntityClasses(project));
105
// } catch (IOException e) {
106
// ErrorManager.getDefault().notify(e);
107
// }
108
// processEntities(PersistenceUtils.getAnnotationEntityClasses(project));
109
}
110
111     private void processEntities(Set JavaDoc<Entity> entityClasses) {
112         for (Entity entity : entityClasses) {
113             Table entityTable = entity.getTable();
114             if (entityTable != null) {
115                 entityName2TableName.put(entityTable.getName(), entity.getClass2());
116             }
117         }
118     }
119
120     public void uninit() {
121     }
122
123     public String JavaDoc getFQClassName(String JavaDoc tableName) {
124         return entityName2TableName.get(tableName);
125     }
126
127     public String JavaDoc generateEntityName(String JavaDoc name) {
128         return name;
129     }
130
131     public Set JavaDoc createdObjects() {
132         return Collections.EMPTY_SET;
133     }
134
135     /**
136      * Encapsulates the whole entity class generation process.
137      */

138     private static final class Generator {
139
140         private final RelatedCMPHelper helper;
141         private final ProgressHandle handle;
142         private final ProgressPanel progressPanel;
143         private final Map JavaDoc<String JavaDoc, EntityClass> beanMap = new HashMap JavaDoc<String JavaDoc, EntityClass>();
144
145         public Generator(RelatedCMPHelper helper, ProgressHandle handle, ProgressPanel progressPanel) {
146             this.helper = helper;
147             this.handle = handle;
148             this.progressPanel = progressPanel;
149         }
150
151         public void run() throws IOException JavaDoc {
152             EntityClass[] genBeans = helper.getBeans();
153
154             int progressMax = genBeans.length * 2;
155             handle.switchToDeterminate(progressMax);
156
157             // first generate empty entity classes -- this is needed as
158
// in the field and method generation it will be necessary to resolve
159
// their types (e.g. entity A has a field of type Collection<B>, thus
160
// while generating entity A we must be able to resolve type B).
161

162             beanMap.clear();
163             Set JavaDoc<FileObject> generationPackageFOs = new HashSet JavaDoc<FileObject>();
164             Set JavaDoc<String JavaDoc> generatedEntityClasses = new HashSet JavaDoc<String JavaDoc>();
165             for (int i = 0; i < genBeans.length; i++) {
166                 final EntityClass entityClass = genBeans[i];
167                 String JavaDoc entityClassName = entityClass.getClassName();
168                 FileObject packageFileObject = genBeans[i].getPackageFileObject();
169                 beanMap.put(entityClassName, entityClass);
170
171                 if (packageFileObject.getFileObject(entityClassName, "java") != null) { // NOI18N
172
handle.progress(i);
173                     continue;
174                 }
175                 String JavaDoc progressMsg = NbBundle.getMessage(JavaPersistenceGenerator.class, "TXT_GeneratingClass", entityClassName);
176                 handle.progress(progressMsg, i);
177                 progressPanel.setText(progressMsg);
178
179                 generationPackageFOs.add(packageFileObject);
180                 generatedEntityClasses.add(entityClassName);
181
182                 // XXX Javadoc
183
GenerationUtils.createClass(packageFileObject, entityClassName, NbBundle.getMessage(JavaPersistenceGenerator.class, "MSG_Javadoc_Class"));
184                 if (!genBeans[i].isUsePkField()) {
185                     String JavaDoc pkClassName = createPKClassName(entityClassName);
186                     GenerationUtils.createClass(packageFileObject, pkClassName, NbBundle.getMessage(JavaPersistenceGenerator.class, "MSG_Javadoc_PKClass", pkClassName, entityClassName));
187                 }
188             }
189
190             // now generate the fields and methods for each entity class
191
// and its primary key class
192

193
194             for (int i = 0; i < genBeans.length; i++) {
195                 final EntityClass entityClass = genBeans[i];
196                 String JavaDoc entityClassName = entityClass.getClassName();
197
198                 if (!generatedEntityClasses.contains(entityClassName)) {
199                     // this entity class already existed, we didn't create it, so we don't want to touch it
200
handle.progress(genBeans.length + i);
201                     continue;
202                 }
203                 String JavaDoc progressMsg = NbBundle.getMessage(JavaPersistenceGenerator.class, "TXT_GeneratingClass", entityClassName);
204                 handle.progress(progressMsg, genBeans.length + i);
205                 progressPanel.setText(progressMsg);
206
207                 FileObject entityClassPackageFO = entityClass.getPackageFileObject();
208                 final FileObject entityClassFO = entityClassPackageFO.getFileObject(entityClassName, "java"); // NOI18N
209
final FileObject pkClassFO = entityClassPackageFO.getFileObject(createPKClassName(entityClassName), "java"); // NOI18N
210
try {
211                     
212                     Set JavaDoc<ClassPath> bootCPs = getAllClassPaths(generationPackageFOs, ClassPath.BOOT);
213                     Set JavaDoc<ClassPath> compileCPs = getAllClassPaths(generationPackageFOs, ClassPath.COMPILE);
214                     Set JavaDoc<ClassPath> sourceCPs = getAllClassPaths(generationPackageFOs, ClassPath.SOURCE);
215                     
216                     JPAClassPathHelper cpHelper = new JPAClassPathHelper(bootCPs, compileCPs, sourceCPs);
217
218                     JavaSource javaSource = (pkClassFO != null) ?
219                         JavaSource.create(cpHelper.createClasspathInfo(), entityClassFO, pkClassFO) :
220                         JavaSource.create(cpHelper.createClasspathInfo(), entityClassFO);
221                     javaSource.runModificationTask(new AbstractTask<WorkingCopy>() {
222                         public void run(WorkingCopy copy) throws IOException JavaDoc {
223                             if (copy.getFileObject().equals(entityClassFO)) {
224                                 new EntityClassGenerator(helper, copy, entityClass).run();
225                             } else {
226                                 new PKClassGenerator(helper, copy, entityClass).run();
227                             }
228                         }
229                     }).commit();
230                 } catch (IOException JavaDoc e) {
231                     String JavaDoc message = e.getMessage();
232                     String JavaDoc newMessage = ((message == null) ?
233                         NbBundle.getMessage(JavaPersistenceGenerator.class, "ERR_GeneratingClass_NoExceptionMessage", entityClassName) :
234                         NbBundle.getMessage(JavaPersistenceGenerator.class, "ERR_GeneratingClass", entityClassName, message));
235                     IOException JavaDoc wrappedException = new IOException JavaDoc(newMessage);
236                     wrappedException.initCause(e);
237                     throw wrappedException;
238                 }
239
240                 Project project = FileOwnerQuery.getOwner(entityClassFO);
241                 if (!Util.isSupportedJavaEEVersion(project) && ProviderUtil.getDDFile(project) != null) {
242                     try {
243                         PUDataObject pudo = ProviderUtil.getPUDataObject(project);
244                         PersistenceUnit pu[] = pudo.getPersistence().getPersistenceUnit();
245                         //only add if a PU exists, if there are more we do not know where to add - UI needed to ask
246
if (pu.length == 1) {
247                             pudo.addClass(pu[0], entityClassName);
248                         }
249                     } catch (InvalidPersistenceXmlException ipx){
250                         // just log for debugging purposes, at this point the user has
251
// already been warned about an invalid persistence.xml
252
Logger.getLogger(JavaPersistenceGenerator.class.getName()).log(Level.FINE, "Invalid persistence.xml: " + ipx.getPath(), ipx); //NO18N
253
}
254                 }
255             }
256
257             handle.progress(progressMax);
258         }
259
260         private static String JavaDoc createPKClassName(String JavaDoc entityClassName) {
261             return entityClassName + "PK"; // NOI18N
262
}
263
264         private static Set JavaDoc<ClassPath> getAllClassPaths(Set JavaDoc<FileObject> fileObjects, String JavaDoc id) {
265             Set JavaDoc<ClassPath> classPaths = new HashSet JavaDoc<ClassPath>();
266             for (FileObject fileObject : fileObjects) {
267                 classPaths.add(ClassPath.getClassPath(fileObject, id));
268             }
269             return classPaths;
270         }
271
272
273         /**
274          * Encapsulates common logic for generating classes (be it
275          * entity or primary key classes). Each instance generates a single
276          * class.
277          */

278         private abstract class ClassGenerator {
279
280             protected final RelatedCMPHelper helper;
281             protected final WorkingCopy copy;
282             protected final GenerationUtils genUtils;
283
284             // the entity class we are generating
285
protected final EntityClass entityClass;
286             // the mapping of the entity class to the database
287
protected final CMPMappingModel dbMappings;
288             // true if a primary key class needs to be generated along with the entity class
289
protected final boolean needsPKClass;
290             // the simple class name of the primary key class
291
protected final String JavaDoc pkClassName;
292             // the fully-qualified name of the primary key class
293
protected final String JavaDoc pkFQClassName;
294
295             // generated properties
296
protected final List JavaDoc<Property> properties = new ArrayList JavaDoc<Property>();
297             // generated methods
298
protected final List JavaDoc<MethodTree> methods = new ArrayList JavaDoc<MethodTree>();
299             // generated constructors
300
protected final List JavaDoc<MethodTree> constructors = new ArrayList JavaDoc<MethodTree>();
301
302             // the class tree of the class we are generating
303
protected ClassTree classTree;
304
305             public ClassGenerator(RelatedCMPHelper helper, WorkingCopy copy, EntityClass entityClass) throws IOException JavaDoc {
306                 this.helper = helper;
307                 this.copy = copy;
308
309                 this.entityClass = entityClass;
310                 dbMappings = entityClass.getCMPMapping();
311                 needsPKClass = !entityClass.isUsePkField();
312                 pkClassName = needsPKClass ? createPKClassName(entityClass.getClassName()) : null;
313                 pkFQClassName = entityClass.getPackage() + "." + pkClassName; // NOI18N
314

315                 genUtils = GenerationUtils.newInstance(copy);
316                 if (genUtils == null) {
317                     throw new IllegalStateException JavaDoc("Cannot find a public top-level class named " + entityClass.getClassName() + // NOI18N
318
" in " + FileUtil.getFileDisplayName(copy.getFileObject())); // NOI18N
319
}
320                 classTree = genUtils.getClassTree();
321             }
322
323             protected String JavaDoc createFieldName(String JavaDoc capitalizedFieldName) {
324                 return createFieldNameImpl(capitalizedFieldName, false);
325             }
326
327             protected String JavaDoc createCapitalizedFieldName(String JavaDoc fieldName) {
328                 return createFieldNameImpl(fieldName, true);
329             }
330
331             private String JavaDoc createFieldNameImpl(String JavaDoc fieldName, boolean capitalized) {
332                 StringBuffer JavaDoc sb = new StringBuffer JavaDoc(fieldName);
333                 char firstChar = sb.charAt(0);
334                 sb.setCharAt(0, capitalized ? Character.toUpperCase(firstChar) : Character.toLowerCase(firstChar));
335                 return sb.toString();
336             }
337
338             /**
339              * Creates a property for an entity member, that is, is creates
340              * a field, a getter and a setter method.
341              */

342             protected Property createProperty(EntityMember m) throws IOException JavaDoc {
343                 boolean isPKMember = m.isPrimaryKey();
344                 List JavaDoc<AnnotationTree> annotations = new ArrayList JavaDoc<AnnotationTree>();
345
346                 //add @Id() only if not in an embeddable PK class
347
if (isPKMember && !needsPKClass) {
348                     annotations.add(genUtils.createAnnotation("javax.persistence.Id")); // NOI18N
349
}
350
351                 boolean isLobType = m.isLobType();
352                 if (isLobType) {
353                     annotations.add(genUtils.createAnnotation("javax.persistence.Lob")); // NOI18N
354
}
355
356                 List JavaDoc<ExpressionTree> columnAnnArguments = new ArrayList JavaDoc();
357                 String JavaDoc memberName = m.getMemberName();
358
359                 String JavaDoc columnName = (String JavaDoc) dbMappings.getCMPFieldMapping().get(memberName);
360                 columnAnnArguments.add(genUtils.createAnnotationArgument("name", columnName)); //NOI18N
361
if (!m.isNullable()) {
362                     columnAnnArguments.add(genUtils.createAnnotationArgument("nullable", false)); //NOI18N
363
}
364                 annotations.add(genUtils.createAnnotation("javax.persistence.Column", columnAnnArguments)); //NOI18N
365

366                 String JavaDoc temporalType = getMemberTemporalType(m);
367                 if (temporalType != null) {
368                     ExpressionTree temporalAnnValueArgument = genUtils.createAnnotationArgument(null, "javax.persistence.TemporalType", temporalType); //NOI18N
369
annotations.add(genUtils.createAnnotation("javax.persistence.Temporal", Collections.singletonList(temporalAnnValueArgument)));
370                 }
371
372                 return new Property(Modifier.PRIVATE, annotations, getMemberType(m), memberName);
373             }
374
375             /**
376              * Like {@link #createProperty}, but it only creates a variable
377              * with no modififers and no annotations. Useful to pass in
378              * a parameter list when creating a method or constructor.
379              */

380             protected VariableTree createVariable(EntityMember m) {
381                 return genUtils.createVariable(m.getMemberName(), getMemberType(m));
382             }
383
384             private String JavaDoc getMemberType(EntityMember m) {
385                 String JavaDoc memberType = m.getMemberType();
386                 if ("java.sql.Date".equals(memberType)) { //NOI18N
387
memberType = "java.util.Date";
388                 } else if ("java.sql.Time".equals(memberType)) { //NOI18N
389
memberType = "java.util.Date";
390                 } else if ("java.sql.Timestamp".equals(memberType)) { //NOI18N
391
memberType = "java.util.Date";
392                 }
393                 return memberType;
394             }
395
396             private String JavaDoc getMemberTemporalType(EntityMember m) {
397                 String JavaDoc memberType = m.getMemberType();
398                 String JavaDoc temporalType = null;
399                 if ("java.sql.Date".equals(memberType)) { //NOI18N
400
temporalType = "DATE";
401                 } else if ("java.sql.Time".equals(memberType)) { //NOI18N
402
temporalType = "TIME";
403                 } else if ("java.sql.Timestamp".equals(memberType)) { //NOI18N
404
temporalType = "TIMESTAMP";
405                 }
406                 return temporalType;
407             }
408
409             protected MethodTree createHashCodeMethod(List JavaDoc<VariableTree> fields) {
410                 StringBuilder JavaDoc body = new StringBuilder JavaDoc(20 + fields.size() * 30);
411                 body.append("{"); // NOI18N
412
body.append("int hash = 0;"); // NOI18N
413
for (VariableTree field : fields) {
414                     body.append(createHashCodeLineForField(field));
415                 }
416                 body.append("return hash;"); // NOI18N
417
body.append("}"); // NOI18N
418
TreeMaker make = copy.getTreeMaker();
419                 // XXX Javadoc
420
return make.Method(
421                         make.Modifiers(EnumSet.of(Modifier.PUBLIC), Collections.singletonList(genUtils.createAnnotation("java.lang.Override"))),
422                         "hashCode", // NOI18N
423
make.PrimitiveType(TypeKind.INT),
424                         Collections.<TypeParameterTree>emptyList(),
425                         Collections.<VariableTree>emptyList(),
426                         Collections.<ExpressionTree>emptyList(),
427                         body.toString(),
428                         null);
429             }
430
431             private String JavaDoc createHashCodeLineForField(VariableTree field) {
432                 Name fieldName = field.getName();
433                 Tree fieldType = field.getType();
434                 if (fieldType.getKind() == Tree.Kind.PRIMITIVE_TYPE) {
435                     if (((PrimitiveTypeTree)fieldType).getPrimitiveTypeKind() == TypeKind.BOOLEAN) {
436                         return "hash += (" + fieldName + " ? 1 : 0"; // NOI18N
437
}
438                     return "hash += (int)" + fieldName + ";"; // NOI18N
439
}
440                 return "hash += (" + fieldName + " != null ? " + fieldName + ".hashCode() : 0);"; // NOI18N
441
}
442
443             protected MethodTree createEqualsMethod(String JavaDoc simpleClassName, List JavaDoc<VariableTree> fields) {
444                 StringBuilder JavaDoc body = new StringBuilder JavaDoc(50 + fields.size() * 30);
445                 body.append("{"); // NOI18N
446
body.append("// TODO: Warning - this method won't work in the case the id fields are not set\n"); // NOI18N
447
body.append("if (!(object instanceof "); // NOI18N
448
body.append(simpleClassName + ")) {return false;}"); // NOI18N
449
body.append(simpleClassName + " other = (" + simpleClassName + ")object;"); // NOI18N
450
for (VariableTree field : fields) {
451                     body.append(createEqualsLineForField(field));
452                 }
453                 body.append("return true;"); // NOI18N
454
body.append("}"); // NOI18N
455
TreeMaker make = copy.getTreeMaker();
456                 // XXX Javadoc
457
return make.Method(
458                         make.Modifiers(EnumSet.of(Modifier.PUBLIC), Collections.singletonList(genUtils.createAnnotation("java.lang.Override"))), // NOI18N
459
"equals", // NOI18N
460
make.PrimitiveType(TypeKind.BOOLEAN),
461                         Collections.<TypeParameterTree>emptyList(),
462                         Collections.singletonList(genUtils.createVariable("object", "java.lang.Object")), // NOI18N
463
Collections.<ExpressionTree>emptyList(),
464                         body.toString(),
465                         null);
466             }
467
468             private String JavaDoc createEqualsLineForField(VariableTree field){
469                 Name fieldName = field.getName();
470                 Tree fieldType = field.getType();
471                 if (fieldType.getKind() == Tree.Kind.PRIMITIVE_TYPE) {
472                     return "if (this." + fieldName + " != other." + fieldName + ") return false;"; // NOI18N
473
}
474                 return "if (this." + fieldName + " != other." + fieldName + // NOI18N
475
" && (this." + fieldName + " == null || !this." + // NOI18N
476
fieldName + ".equals(other." + fieldName + "))) return false;"; // NOI18N
477
}
478
479             protected MethodTree createToStringMethod(String JavaDoc simpleClassName, List JavaDoc<VariableTree> fields) {
480                 StringBuilder JavaDoc body = new StringBuilder JavaDoc(30 + fields.size() * 30);
481                 body.append("{"); // NOI18N
482
body.append("return \"" + simpleClassName + "["); // NOI18N
483
for (Iterator JavaDoc<VariableTree> i = fields.iterator(); i.hasNext();) {
484                     String JavaDoc fieldName = i.next().getName().toString();
485                     body.append(fieldName + "=\" + " + fieldName + " + \""); //NOI18N
486
body.append(i.hasNext() ? ", " : "]\";"); //NOI18N
487
}
488                 body.append("}"); // NOI18N
489
TreeMaker make = copy.getTreeMaker();
490                 // XXX Javadoc
491
return make.Method(
492                         make.Modifiers(EnumSet.of(Modifier.PUBLIC), Collections.singletonList(genUtils.createAnnotation("java.lang.Override"))),
493                         "toString", // NOI18N
494
genUtils.createType("java.lang.String"), // NOI18N
495
Collections.<TypeParameterTree>emptyList(),
496                         Collections.<VariableTree>emptyList(),
497                         Collections.<ExpressionTree>emptyList(),
498                         body.toString(),
499                         null);
500             }
501
502             public void run() throws IOException JavaDoc {
503                 initialize();
504                 for (Object JavaDoc object : entityClass.getFields()) {
505                     generateMember((EntityMember)object);
506                 }
507                 afterMembersGenerated();
508                 for (Object JavaDoc object : entityClass.getRoles()) {
509                     generateRelationship((RelationshipRole)object);
510                 }
511                 finish();
512
513                 // add the generated members
514
TreeMaker make = copy.getTreeMaker();
515                 int position = 0;
516                 for (Property property : properties) {
517                     classTree = make.insertClassMember(classTree, position, property.getField());
518                     position++;
519                 }
520                 for (MethodTree constructor : constructors) {
521                     classTree = make.addClassMember(classTree, constructor);
522                 }
523                 for (Property property : properties) {
524                     classTree = make.addClassMember(classTree, property.getGetter());
525                     classTree = make.addClassMember(classTree, property.getSetter());
526                 }
527                 for (MethodTree method : methods) {
528                     classTree = make.addClassMember(classTree, method);
529                 }
530                 copy.rewrite(genUtils.getClassTree(), classTree);
531             }
532
533             /**
534              * Called at the beginning of the generation process.
535              */

536             protected abstract void initialize() throws IOException JavaDoc;
537
538             /**
539              * Called for each entity class member.
540              */

541             protected abstract void generateMember(EntityMember m) throws IOException JavaDoc;
542
543             /**
544              * Called after all members have been generated.
545              */

546             protected abstract void afterMembersGenerated() throws IOException JavaDoc;
547
548             /**
549              * Called for each relationship.
550              */

551             protected abstract void generateRelationship(RelationshipRole role) throws IOException JavaDoc;
552
553             /**
554              * Called at the end of the generation process.
555              */

556             protected abstract void finish() throws IOException JavaDoc;
557
558             /**
559              * Encapsulates a generated property, that is, its field, getter
560              * and setter method.
561              */

562             protected final class Property {
563
564                 private final VariableTree field;
565                 private final MethodTree getter;
566                 private final MethodTree setter;
567
568                 public Property(Modifier modifier, List JavaDoc<AnnotationTree> annotations, String JavaDoc type, String JavaDoc name) throws IOException JavaDoc {
569                     this(modifier, annotations, genUtils.createType(type), name);
570                 }
571
572                 public Property(Modifier modifier, List JavaDoc<AnnotationTree> annotations, TypeMirror type, String JavaDoc name) throws IOException JavaDoc {
573                     this(modifier, annotations, copy.getTreeMaker().Type(type), name);
574                 }
575
576                 private Property(Modifier modifier, List JavaDoc<AnnotationTree> annotations, Tree typeTree, String JavaDoc name) throws IOException JavaDoc {
577                     TreeMaker make = copy.getTreeMaker();
578                     field = make.Variable(
579                             make.Modifiers(EnumSet.of(modifier), fieldAccess ? annotations : Collections.<AnnotationTree>emptyList()),
580                             name,
581                             typeTree,
582                             null);
583                     getter = genUtils.createPropertyGetterMethod(
584                             make.Modifiers(EnumSet.of(Modifier.PUBLIC), fieldAccess ? Collections.<AnnotationTree>emptyList() : annotations),
585                             name,
586                             typeTree);
587                     setter = genUtils.createPropertySetterMethod(
588                             genUtils.createModifiers(Modifier.PUBLIC),
589                             name,
590                             typeTree);
591                 }
592
593                 public VariableTree getField() {
594                     return field;
595                 }
596
597                 public MethodTree getGetter() {
598                     return getter;
599                 }
600
601                 public MethodTree getSetter() {
602                     return setter;
603                 }
604             }
605         }
606
607         /**
608          * An implementation of ClassGenerator which generates entity classes.
609          */

610         private final class EntityClassGenerator extends ClassGenerator {
611
612             // the simple name of the entity class
613
private final String JavaDoc entityClassName;
614             // the fully-qualified name of the entity class
615
private final String JavaDoc entityFQClassName;
616             // the non-nullable properties (not including the primary key ones)
617
private final List JavaDoc<Property> nonNullableProps = new ArrayList JavaDoc<Property>();
618             // the names of the primary key columns
619
private final List JavaDoc<String JavaDoc> pkColumnNames = new ArrayList JavaDoc<String JavaDoc>();
620             // variables correspoding to the fields in the primary key classs (or empty if no primary key class)
621
private final List JavaDoc<VariableTree> pkClassVariables = new ArrayList JavaDoc<VariableTree>();
622             // the list of @NamedQuery annotations which will be added to the entity class
623
private final List JavaDoc<ExpressionTree> namedQueryAnnotations = new ArrayList JavaDoc<ExpressionTree>();
624
625             // the property for the primary key (or the primary key class)
626
private Property pkProperty;
627             // the prefix or all named queries ("select ... ")
628
private String JavaDoc namedQueryPrefix;
629
630             public EntityClassGenerator(RelatedCMPHelper helper, WorkingCopy copy, EntityClass entityClass) throws IOException JavaDoc {
631                 super(helper, copy, entityClass);
632                 entityClassName = entityClass.getClassName();
633                 assert genUtils.getTypeElement().getSimpleName().contentEquals(entityClassName);
634                 entityFQClassName = entityClass.getPackage() + "." + entityClassName;
635             }
636
637             protected void initialize() throws IOException JavaDoc {
638                 classTree = genUtils.ensureNoArgConstructor(classTree);
639                 if (genSerializableEntities) {
640                     classTree = genUtils.addImplementsClause(classTree, "java.io.Serializable"); // NOI18N
641
}
642                 classTree = genUtils.addAnnotation(classTree, genUtils.createAnnotation("javax.persistence.Entity")); // NOI18N
643
ExpressionTree tableNameArgument = genUtils.createAnnotationArgument("name", dbMappings.getTableName()); // NOI18N
644
classTree = genUtils.addAnnotation(classTree, genUtils.createAnnotation("javax.persistence.Table", Collections.singletonList(tableNameArgument)));
645
646                 if (needsPKClass) {
647                     String JavaDoc pkFieldName = createFieldName(pkClassName);
648                     pkProperty = new Property(
649                             Modifier.PROTECTED,
650                             Collections.singletonList(genUtils.createAnnotation("javax.persistence.EmbeddedId")),
651                             pkFQClassName,
652                             pkFieldName);
653                     properties.add(pkProperty);
654                 }
655
656                 //TODO: javadoc - generate or fake in test mode
657
// b.setCommentDataAuthor(authorOverride);
658
// b.setCommentDataDate(dateOverride);
659
}
660
661             protected void generateMember(EntityMember m) throws IOException JavaDoc {
662                 String JavaDoc memberName = m.getMemberName();
663                 boolean isPKMember = m.isPrimaryKey();
664                 Property property = null;
665                 if (isPKMember) {
666                     if (needsPKClass) {
667                         pkClassVariables.add(createVariable(m));
668                     } else {
669                         pkProperty = property = createProperty(m);
670                     }
671                     String JavaDoc pkColumnName = (String JavaDoc)dbMappings.getCMPFieldMapping().get(memberName);
672                     pkColumnNames.add(pkColumnName);
673                 } else {
674                     property = createProperty(m);
675                     if (!m.isNullable()) {
676                         nonNullableProps.add(property);
677                     }
678                 }
679                 // we don't create the property only if the current member is
680
// part of a primary key, in which case it will be put in the primary key class
681
assert (property != null) || (property == null && isPKMember && needsPKClass);
682                 if (property != null) {
683                     properties.add(property);
684                 }
685
686                 // generate equivalent of finder methods - named query annotations
687
if (helper.isGenerateFinderMethods() && !m.isLobType()) {
688                     List JavaDoc<ExpressionTree> namedQueryAnnArguments = new ArrayList JavaDoc<ExpressionTree>();
689                     namedQueryAnnArguments.add(genUtils.createAnnotationArgument("name", entityClassName + ".findBy" + createCapitalizedFieldName(memberName))); //NOI18N
690

691                     if (namedQueryPrefix == null) {
692                         char firstLetter = entityClassName.toLowerCase().charAt(0);
693                         namedQueryPrefix = "SELECT " + firstLetter + " FROM " + entityClassName + " " + firstLetter + " WHERE " + firstLetter + "."; // NOI18N
694
}
695                     // need a prefix of "pk_field_name." if this is part of a composite pk
696
String JavaDoc memberAccessString = ((needsPKClass && isPKMember) ? (pkProperty.getField().getName().toString() + "." + memberName) : memberName); // NOI18N
697
namedQueryAnnArguments.add(genUtils.createAnnotationArgument(
698                             "query", namedQueryPrefix + //NOI18N
699
memberAccessString + ((genNamedParams) ? (" = :" + memberName) : "= ?1"))); //NOI18N
700
namedQueryAnnotations.add(genUtils.createAnnotation("javax.persistence.NamedQuery", namedQueryAnnArguments)); //NOI18N
701
}
702             }
703
704             protected void afterMembersGenerated() {
705                 classTree = genUtils.addAnnotation(classTree, genUtils.createAnnotation("javax.persistence.NamedQueries", // NOI18N
706
Collections.singletonList(genUtils.createAnnotationArgument(null, namedQueryAnnotations))));
707             }
708
709             protected void generateRelationship(RelationshipRole role) throws IOException JavaDoc {
710                 String JavaDoc memberName = role.getFieldName();
711
712                 // XXX getRelationshipFieldType() does not work well when entity classes
713
// are not all generated to the same package
714
String JavaDoc typeName = getRelationshipFieldType(role, entityClass.getPackage());
715                 TypeMirror fieldType = copy.getElements().getTypeElement(typeName).asType();
716                 if (role.isToMany()) {
717                     // XXX this will probably not resolve imports
718
TypeElement collectionType = copy.getElements().getTypeElement("java.util.Collection"); // NOI18N
719
fieldType = copy.getTypes().getDeclaredType(collectionType, fieldType);
720                 }
721
722                 List JavaDoc<AnnotationTree> annotations = new ArrayList JavaDoc<AnnotationTree>();
723                 List JavaDoc<ExpressionTree> annArguments = new ArrayList JavaDoc<ExpressionTree>();
724                 if (role.isCascade()) {
725                     annArguments.add(genUtils.createAnnotationArgument("cascade", "javax.persistence.CascadeType", "ALL")); // NOI18N
726
}
727                 if (role.equals(role.getParent().getRoleB())) {
728                     annArguments.add(genUtils.createAnnotationArgument("mappedBy", role.getParent().getRoleA().getFieldName())); // NOI18N
729
} else {
730                     if (role.isMany() && role.isToMany()) {
731                         List JavaDoc<ExpressionTree> joinTableAnnArguments = new ArrayList JavaDoc<ExpressionTree>();
732                         joinTableAnnArguments.add(genUtils.createAnnotationArgument("name", (String JavaDoc) dbMappings.getJoinTableMapping().get(role.getFieldName()))); //NOI18N
733

734                         CMPMappingModel.JoinTableColumnMapping joinColumnMap = dbMappings.getJoinTableColumnMppings().get(role.getFieldName());
735
736                         List JavaDoc<AnnotationTree> joinCols = new ArrayList JavaDoc<AnnotationTree>();
737                         String JavaDoc[] colNames = joinColumnMap.getColumns();
738                         String JavaDoc[] refColNames = joinColumnMap.getReferencedColumns();
739                         for(int colIndex = 0; colIndex < colNames.length; colIndex++) {
740                             List JavaDoc<ExpressionTree> attrs = new ArrayList JavaDoc<ExpressionTree>();
741                             attrs.add(genUtils.createAnnotationArgument("name", colNames[colIndex])); //NOI18N
742
attrs.add(genUtils.createAnnotationArgument("referencedColumnName", refColNames[colIndex])); //NOI18N
743
joinCols.add(genUtils.createAnnotation("javax.persistence.JoinColumn", attrs)); //NOI18N
744
}
745                         joinTableAnnArguments.add(genUtils.createAnnotationArgument("joinColumns", joinCols)); // NOI18N
746

747                         List JavaDoc<AnnotationTree> inverseCols = new ArrayList JavaDoc<AnnotationTree>();
748                         String JavaDoc[] invColNames = joinColumnMap.getInverseColumns();
749                         String JavaDoc[] refInvColNames = joinColumnMap.getReferencedInverseColumns();
750                         for(int colIndex = 0; colIndex < invColNames.length; colIndex++) {
751                             List JavaDoc<ExpressionTree> attrs = new ArrayList JavaDoc<ExpressionTree>();
752                             attrs.add(genUtils.createAnnotationArgument("name", invColNames[colIndex])); //NOI18N
753
attrs.add(genUtils.createAnnotationArgument("referencedColumnName", refInvColNames[colIndex])); //NOI18N
754
inverseCols.add(genUtils.createAnnotation("javax.persistence.JoinColumn", attrs)); // NOI18N
755
}
756                         joinTableAnnArguments.add(genUtils.createAnnotationArgument("inverseJoinColumns", inverseCols)); // NOI18N
757

758                         annotations.add(genUtils.createAnnotation("javax.persistence.JoinTable", joinTableAnnArguments)); // NOI18N
759
} else {
760                         String JavaDoc[] colNames = (String JavaDoc[]) dbMappings.getCmrFieldMapping().get(role.getFieldName());
761                         CMPMappingModel relatedMappings = beanMap.get(role.getParent().getRoleB().getEntityName()).getCMPMapping();
762                         String JavaDoc[] invColNames = (String JavaDoc[]) relatedMappings.getCmrFieldMapping().get(role.getParent().getRoleB().getFieldName());
763                         if (colNames.length == 1) {
764                             List JavaDoc<ExpressionTree> attrs = new ArrayList JavaDoc<ExpressionTree>();
765                             attrs.add(genUtils.createAnnotationArgument("name", colNames[0])); //NOI18N
766
attrs.add(genUtils.createAnnotationArgument("referencedColumnName", invColNames[0])); //NOI18N
767
makeReadOnlyIfNecessary(pkColumnNames, colNames[0], attrs);
768                             annotations.add(genUtils.createAnnotation("javax.persistence.JoinColumn", attrs)); //NOI18N
769
} else {
770                             List JavaDoc<AnnotationTree> joinCols = new ArrayList JavaDoc<AnnotationTree>();
771                             for(int colIndex = 0; colIndex < colNames.length; colIndex++) {
772                                 List JavaDoc<ExpressionTree> attrs = new ArrayList JavaDoc<ExpressionTree>();
773                                 attrs.add(genUtils.createAnnotationArgument("name", colNames[colIndex])); //NOI18N
774
attrs.add(genUtils.createAnnotationArgument("referencedColumnName", invColNames[colIndex])); //NOI18N
775
makeReadOnlyIfNecessary(pkColumnNames, colNames[colIndex], attrs);
776                                 joinCols.add(genUtils.createAnnotation("javax.persistence.JoinColumn", attrs)); // NOI18N
777
}
778                             ExpressionTree joinColumnsNameAttrValue = genUtils.createAnnotationArgument(null, joinCols);
779                             AnnotationTree joinColumnsAnnotation = genUtils.createAnnotation("javax.persistence.JoinColumns", Collections.singletonList(joinColumnsNameAttrValue)); //NOI18N
780
annotations.add(joinColumnsAnnotation);
781                         }
782                     }
783                 }
784                 String JavaDoc relationAnn;
785                 if (role.isMany() && role.isToMany()) {
786                     relationAnn = "ManyToMany"; //NOI18N
787
} else if (role.isMany()) {
788                     relationAnn = "ManyToOne"; //NOI18N
789
} else if (role.isToMany()) {
790                     relationAnn = "OneToMany"; //NOI18N
791
} else {
792                     relationAnn = "OneToOne"; //NOI18N
793
}
794                 annotations.add(genUtils.createAnnotation("javax.persistence." + relationAnn, annArguments)); // NOI18N
795

796                 properties.add(new Property(Modifier.PRIVATE, annotations, fieldType, memberName));
797             }
798
799             protected void finish() {
800                 // create a constructor which takes the primary key field as argument
801
VariableTree pkFieldParam = genUtils.removeModifiers(pkProperty.getField());
802                 List JavaDoc<VariableTree> pkFieldParams = Collections.singletonList(pkFieldParam);
803                 constructors.add(genUtils.createAssignmentConstructor(genUtils.createModifiers(Modifier.PUBLIC), entityClassName, pkFieldParams));
804
805                 // if different than pk fields constructor, add constructor
806
// which takes all non-nullable non-relationship fields as args
807
if (nonNullableProps.size() > 0) {
808                     List JavaDoc<VariableTree> nonNullableParams = new ArrayList JavaDoc<VariableTree>(nonNullableProps.size() + 1);
809                     nonNullableParams.add(pkFieldParam);
810                     for (Property property : nonNullableProps) {
811                         nonNullableParams.add(genUtils.removeModifiers(property.getField()));
812                     }
813                     constructors.add(genUtils.createAssignmentConstructor(genUtils.createModifiers(Modifier.PUBLIC), entityClassName, nonNullableParams));
814                 }
815
816                 // create a constructor which takes the fields of the primary key class as arguments
817
if (pkClassVariables.size() > 0) {
818                     StringBuilder JavaDoc body = new StringBuilder JavaDoc(30 + 30 * pkClassVariables.size());
819                     body.append("{"); // NOI18N
820
body.append("this." + pkProperty.getField().getName() + " = new " + pkClassName + "("); // NOI18N
821
for (Iterator JavaDoc<VariableTree> i = pkClassVariables.iterator(); i.hasNext();) {
822                         body.append(i.next().getName());
823                         body.append(i.hasNext() ? ", " : ");"); // NOI18N
824
}
825                     body.append("}"); // NOI18N
826
TreeMaker make = copy.getTreeMaker();
827                     constructors.add(make.Constructor(
828                             make.Modifiers(EnumSet.of(Modifier.PUBLIC), Collections.<AnnotationTree>emptyList()),
829                             Collections.<TypeParameterTree>emptyList(),
830                             pkClassVariables,
831                             Collections.<ExpressionTree>emptyList(),
832                             body.toString()));
833                 }
834
835                 // add equals and hashCode methods
836
methods.add(createHashCodeMethod(pkFieldParams));
837                 methods.add(createEqualsMethod(entityClassName, pkFieldParams));
838                 methods.add(createToStringMethod(entityFQClassName, pkFieldParams));
839             }
840
841             private String JavaDoc getRelationshipFieldType(RelationshipRole role, String JavaDoc pkg) {
842                 RelationshipRole rA = role.getParent().getRoleA();
843                 RelationshipRole rB = role.getParent().getRoleB();
844                 RelationshipRole otherRole = role.equals(rA) ? rB : rA;
845                 return pkg + "." + otherRole.getEntityName(); // NOI18N
846
}
847
848             private void makeReadOnlyIfNecessary(List JavaDoc<String JavaDoc> pkColumnNames, String JavaDoc testColumnName, List JavaDoc<ExpressionTree> attrs) {
849                 // if the join column is a pk column, add insertable = false, updatable = false
850
if (pkColumnNames.contains(testColumnName)) {
851                     attrs.add(genUtils.createAnnotationArgument("insertable", false)); //NOI18N
852
attrs.add(genUtils.createAnnotationArgument("updatable", false)); //NOI18N
853
}
854             }
855         }
856
857         /**
858          * An implementation of ClassGenerator which generates primary key
859          * classes.
860          */

861         private final class PKClassGenerator extends ClassGenerator {
862
863             public PKClassGenerator(RelatedCMPHelper helper, WorkingCopy copy, EntityClass entityClass) throws IOException JavaDoc {
864                 super(helper, copy, entityClass);
865             }
866
867             protected void initialize() throws IOException JavaDoc {
868                 classTree = genUtils.ensureNoArgConstructor(classTree);
869                 // primary key class must be serializable and @Embeddable
870
classTree = genUtils.addImplementsClause(classTree, "java.io.Serializable"); //NOI18N
871
classTree = genUtils.addAnnotation(classTree, genUtils.createAnnotation("javax.persistence.Embeddable")); // NOI18N
872
}
873
874             protected void generateMember(EntityMember m) throws IOException JavaDoc {
875                 if (!m.isPrimaryKey()) {
876                     return;
877                 }
878                 Property property = createProperty(m);
879                 properties.add(property);
880             }
881
882             protected void afterMembersGenerated() {
883             }
884
885             protected void generateRelationship(RelationshipRole relationship) {
886             }
887
888             protected void finish() {
889                 // add a constructor which takes the fields of the primary key class as arguments
890
List JavaDoc<VariableTree> parameters = new ArrayList JavaDoc<VariableTree>(properties.size());
891                 for (Property property : properties) {
892                     parameters.add(genUtils.removeModifiers(property.getField()));
893                 }
894                 constructors.add(genUtils.createAssignmentConstructor(genUtils.createModifiers(Modifier.PUBLIC), pkClassName, parameters));
895
896                 // add equals and hashCode methods
897
methods.add(createHashCodeMethod(parameters));
898                 methods.add(createEqualsMethod(pkClassName, parameters));
899                 methods.add(createToStringMethod(pkFQClassName, parameters));
900             }
901         }
902     }
903 }
904
Popular Tags