KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cayenne > jpa > conf > EntityMapDefaultsProcessor


1 /*****************************************************************
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements. See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership. The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License. You may obtain a copy of the License at
9  *
10  * http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing,
13  * software distributed under the License is distributed on an
14  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15  * KIND, either express or implied. See the License for the
16  * specific language governing permissions and limitations
17  * under the License.
18  ****************************************************************/

19
20 package org.apache.cayenne.jpa.conf;
21
22 import java.sql.Time JavaDoc;
23 import java.sql.Timestamp JavaDoc;
24 import java.util.Collection JavaDoc;
25 import java.util.Date JavaDoc;
26
27 import javax.persistence.EnumType;
28 import javax.persistence.TemporalType;
29
30 import org.apache.cayenne.jpa.JpaProviderException;
31 import org.apache.cayenne.jpa.map.AccessType;
32 import org.apache.cayenne.jpa.map.JpaAttribute;
33 import org.apache.cayenne.jpa.map.JpaAttributes;
34 import org.apache.cayenne.jpa.map.JpaBasic;
35 import org.apache.cayenne.jpa.map.JpaClassDescriptor;
36 import org.apache.cayenne.jpa.map.JpaColumn;
37 import org.apache.cayenne.jpa.map.JpaEntity;
38 import org.apache.cayenne.jpa.map.JpaEntityMap;
39 import org.apache.cayenne.jpa.map.JpaId;
40 import org.apache.cayenne.jpa.map.JpaJoinColumn;
41 import org.apache.cayenne.jpa.map.JpaManyToMany;
42 import org.apache.cayenne.jpa.map.JpaManyToOne;
43 import org.apache.cayenne.jpa.map.JpaMappedSuperclass;
44 import org.apache.cayenne.jpa.map.JpaOneToMany;
45 import org.apache.cayenne.jpa.map.JpaOneToOne;
46 import org.apache.cayenne.jpa.map.JpaPropertyDescriptor;
47 import org.apache.cayenne.jpa.map.JpaRelationship;
48 import org.apache.cayenne.jpa.map.JpaTable;
49 import org.apache.cayenne.jpa.map.JpaVersion;
50 import org.apache.cayenne.project.ProjectPath;
51 import org.apache.cayenne.util.BaseTreeVisitor;
52 import org.apache.cayenne.util.HierarchicalTreeVisitor;
53 import org.apache.cayenne.util.TraversalUtil;
54 import org.apache.cayenne.util.Util;
55 import org.apache.cayenne.validation.SimpleValidationFailure;
56
57 /**
58  * Initializes JPA specification compatible mapping defaults.
59  *
60  * @author Andrus Adamchik
61  */

62 public class EntityMapDefaultsProcessor {
63
64     protected HierarchicalTreeVisitor visitor;
65     protected transient EntityMapLoaderContext context;
66
67     public void applyDefaults(EntityMapLoaderContext context) throws JpaProviderException {
68         this.context = context;
69
70         if (visitor == null) {
71             visitor = createVisitor();
72         }
73
74         TraversalUtil.traverse(context.getEntityMap(), visitor);
75     }
76
77     /**
78      * Creates a stateless instance of the JpaEntityMap traversal visitor. This method is
79      * lazily invoked and cached by this object.
80      */

81     protected HierarchicalTreeVisitor createVisitor() {
82         return new EntityMapVisitor();
83     }
84
85     abstract class AbstractEntityVisitor extends BaseTreeVisitor {
86
87         AbstractEntityVisitor() {
88             BaseTreeVisitor attributeVisitor = new BaseTreeVisitor();
89             attributeVisitor.addChildVisitor(JpaId.class, new IdVisitor());
90             attributeVisitor.addChildVisitor(JpaBasic.class, new BasicVisitor());
91             attributeVisitor.addChildVisitor(JpaVersion.class, new VersionVisitor());
92             attributeVisitor.addChildVisitor(JpaManyToOne.class, new FKVisitor());
93             attributeVisitor.addChildVisitor(JpaOneToOne.class, new FKVisitor());
94             attributeVisitor.addChildVisitor(
95                     JpaManyToMany.class,
96                     new RelationshipVisitor());
97             attributeVisitor.addChildVisitor(
98                     JpaOneToMany.class,
99                     new RelationshipVisitor());
100
101             addChildVisitor(JpaAttributes.class, attributeVisitor);
102             addChildVisitor(JpaId.class, new IdVisitor());
103         }
104
105         @Override JavaDoc
106         public boolean onStartNode(ProjectPath path) {
107             JpaEntity entity = (JpaEntity) path.getObject();
108
109             // * entity name
110
if (entity.getClassName() == null) {
111                 return false;
112             }
113
114             if (entity.getName() == null) {
115
116                 // use unqualified class name
117
String JavaDoc fqName = entity.getClassName();
118                 int split = fqName.lastIndexOf('.');
119                 entity.setName(split > 0 ? fqName.substring(split + 1) : fqName);
120             }
121
122             if (entity.getAttributes() == null) {
123                 entity.setAttributes(new JpaAttributes());
124             }
125
126             // * default persistent fields
127
JpaClassDescriptor descriptor = entity.getClassDescriptor();
128
129             AccessType access = entity.getAccess();
130             if (access == null) {
131                 access = ((JpaEntityMap) path.getRoot()).getAccess();
132                 entity.setAccess(access);
133             }
134
135             if (access == AccessType.PROPERTY) {
136                 for (JpaPropertyDescriptor candidate : descriptor
137                         .getPropertyDescriptors()) {
138                     processProperty(entity, descriptor, candidate);
139                 }
140             }
141             // field is default...
142
else {
143                 for (JpaPropertyDescriptor candidate : descriptor.getFieldDescriptors()) {
144                     processProperty(entity, descriptor, candidate);
145                 }
146             }
147
148             // * default table (see @Table annotation defaults, JPA spec 9.1.1)
149
if (entity.getTable() == null) {
150                 JpaTable table = new JpaTable(AnnotationPrototypes.getTable());
151
152                 // unclear whether we need to apply any other name transformations ... or
153
// even if we need to upperclas the name. Per default examples looks like
154
// we need.
155
// table.setName(entity.getName().toUpperCase());
156
table.setName(entity.getName());
157                 entity.setTable(table);
158             }
159
160             return true;
161         }
162
163         void processProperty(
164                 JpaEntity entity,
165                 JpaClassDescriptor descriptor,
166                 JpaPropertyDescriptor property) {
167
168             JpaAttributes attributes = entity.getAttributes();
169             if (attributes.getAttribute(property.getName()) != null) {
170                 return;
171             }
172
173             if (property.isDefaultNonRelationalType()) {
174
175                 JpaBasic attribute = new JpaBasic();
176
177                 attribute.setPropertyDescriptor(property);
178                 attribute.setName(property.getName());
179                 attributes.getBasicAttributes().add(attribute);
180             }
181             else {
182                 String JavaDoc path = descriptor.getManagedClass().getName()
183                         + "."
184                         + property.getName();
185                 context.recordConflict(new SimpleValidationFailure(
186                         property.getMember(),
187                         "Undefined property persistence status: " + path));
188             }
189         }
190     }
191
192     class BasicVisitor extends BaseTreeVisitor {
193
194         BasicVisitor() {
195             addChildVisitor(JpaColumn.class, new ColumnVisitor());
196         }
197
198         @Override JavaDoc
199         public boolean onStartNode(ProjectPath path) {
200             JpaBasic jpaBasic = (JpaBasic) path.getObject();
201             if (jpaBasic.getColumn() == null) {
202                 JpaColumn column = new JpaColumn(AnnotationPrototypes.getColumn());
203                 column.setName(jpaBasic.getName());
204                 column.setNullable(jpaBasic.isOptional());
205                 jpaBasic.setColumn(column);
206             }
207
208             JpaEntity entity = (JpaEntity) path.firstInstanceOf(JpaEntity.class);
209
210             // process temporal type defaults
211
if (jpaBasic.getTemporal() == null && jpaBasic.getEnumerated() == null) {
212                 JpaClassDescriptor descriptor = entity.getClassDescriptor();
213                 JpaPropertyDescriptor property = descriptor.getProperty(jpaBasic
214                         .getName());
215
216                 // sanity check
217
if (property == null) {
218                     throw new IllegalStateException JavaDoc("No class property found for name: "
219                             + jpaBasic.getName());
220                 }
221
222                 if (java.sql.Date JavaDoc.class.isAssignableFrom(property.getType())) {
223                     jpaBasic.setTemporal(TemporalType.DATE);
224                 }
225                 else if (Time JavaDoc.class.isAssignableFrom(property.getType())) {
226                     jpaBasic.setTemporal(TemporalType.TIME);
227                 }
228                 else if (Timestamp JavaDoc.class.isAssignableFrom(property.getType())) {
229                     jpaBasic.setTemporal(TemporalType.TIMESTAMP);
230                 }
231                 else if (Date JavaDoc.class.isAssignableFrom(property.getType())) {
232                     jpaBasic.setTemporal(TemporalType.TIMESTAMP);
233                 }
234                 else if (property.getType().isEnum()) {
235                     jpaBasic.setEnumerated(EnumType.ORDINAL);
236                 }
237             }
238
239             return true;
240         }
241     }
242
243     class VersionVisitor extends BasicVisitor {
244
245         @Override JavaDoc
246         public boolean onStartNode(ProjectPath path) {
247             JpaVersion jpaBasic = (JpaVersion) path.getObject();
248             if (jpaBasic.getColumn() == null) {
249                 JpaColumn column = new JpaColumn(AnnotationPrototypes.getColumn());
250                 column.setName(jpaBasic.getName());
251                 jpaBasic.setColumn(column);
252             }
253
254             if (jpaBasic.getTemporal() == null) {
255                 JpaEntity entity = (JpaEntity) path.firstInstanceOf(JpaEntity.class);
256                 JpaClassDescriptor descriptor = entity.getClassDescriptor();
257                 JpaPropertyDescriptor property = descriptor.getProperty(jpaBasic
258                         .getName());
259
260                 if (Date JavaDoc.class.equals(property.getType())) {
261                     jpaBasic.setTemporal(TemporalType.TIMESTAMP);
262                 }
263             }
264
265             return true;
266         }
267     }
268
269     final class ColumnVisitor extends BaseTreeVisitor {
270
271         @Override JavaDoc
272         public boolean onStartNode(ProjectPath path) {
273             JpaColumn column = (JpaColumn) path.getObject();
274
275             JpaAttribute parent = (JpaAttribute) path.firstInstanceOf(JpaAttribute.class);
276
277             if (column.getName() == null) {
278                 column.setName(parent.getName());
279             }
280
281             if (column.getTable() == null) {
282                 JpaEntity entity = (JpaEntity) path.firstInstanceOf(JpaEntity.class);
283                 column.setTable(entity.getTable().getName());
284             }
285
286             if (parent.getPropertyDescriptor().isStringType()) {
287                 if (column.getLength() < 0) {
288                     column.setLength(255);
289                 }
290             }
291             else {
292                 // length for non-string types should be ignored...
293
column.setLength(-1);
294             }
295
296             return true;
297         }
298     }
299
300     final class EntityMapVisitor extends BaseTreeVisitor {
301
302         EntityMapVisitor() {
303             addChildVisitor(JpaEntity.class, new EntityVisitor());
304             addChildVisitor(JpaMappedSuperclass.class, new MappedSuperclassVisitor());
305         }
306
307         @Override JavaDoc
308         public boolean onStartNode(ProjectPath path) {
309             JpaEntityMap entityMap = (JpaEntityMap) path.getObject();
310
311             // TODO: andrus, 4/28/2006 - actually we need to analyze preloaded classes and
312
// see how they were annotated to choose the right access type...
313

314             entityMap.setAccess(AccessType.FIELD);
315
316             return true;
317         }
318     }
319
320     final class EntityVisitor extends AbstractEntityVisitor {
321
322     }
323
324     final class IdVisitor extends BaseTreeVisitor {
325
326         IdVisitor() {
327             addChildVisitor(JpaColumn.class, new ColumnVisitor());
328         }
329
330         @Override JavaDoc
331         public boolean onStartNode(ProjectPath path) {
332             JpaId id = (JpaId) path.getObject();
333
334             if (id.getColumn() == null) {
335
336                 JpaColumn column = new JpaColumn(AnnotationPrototypes.getColumn());
337                 column.setName(id.getName());
338
339                 JpaEntity entity = (JpaEntity) path.firstInstanceOf(JpaEntity.class);
340                 column.setTable(entity.getTable().getName());
341                 id.setColumn(column);
342             }
343
344             return true;
345         }
346     }
347
348     final class JoinColumnVisitor extends BaseTreeVisitor {
349
350         @Override JavaDoc
351         public boolean onStartNode(ProjectPath path) {
352             JpaRelationship relationship = (JpaRelationship) path.getObjectParent();
353             JpaJoinColumn column = (JpaJoinColumn) path.getObject();
354
355             if (column.getTable() == null) {
356                 JpaEntity entity = (JpaEntity) path.firstInstanceOf(JpaEntity.class);
357                 column.setTable(entity.getTable().getName());
358             }
359
360             // JPA Spec, 2.1.8.2 (same for all relationship owners):
361
// The following mapping defaults apply: [...]
362
// Table A contains a foreign key to table B. The foreign key column
363
// name is formed as the concatenation of the following: the name of
364
// the relationship property or field of entityA; "_" ; the name of
365
// the primary key column in table B. The foreign key column has the
366
// same type as the primary key of table B.
367

368             JpaEntityMap map = (JpaEntityMap) path.firstInstanceOf(JpaEntityMap.class);
369             JpaEntity target = map.entityForClass(relationship.getTargetEntityName());
370
371             if (target == null) {
372                 context.recordConflict(new SimpleValidationFailure(
373                         relationship,
374                         "Invalid relationship target "
375                                 + relationship.getTargetEntityName()));
376             }
377             else if (target.getAttributes() == null
378                     || target.getAttributes().getIds().isEmpty()) {
379                 context.recordConflict(new SimpleValidationFailure(
380                         target,
381                         "Relationship target has no PK defined: "
382                                 + relationship.getTargetEntityName()));
383             }
384             else if (target.getAttributes().getIds().size() > 1) {
385                 // TODO: andrus, 4/30/2006 implement this; note that instead of
386
// checking for "attribute.getJoinColumns().isEmpty()" above,
387
// we'll have to match individual columns
388
context.recordConflict(new SimpleValidationFailure(
389                         relationship,
390                         "Defaults for compound FK are not implemented."));
391             }
392             else {
393                 JpaId id = target.getAttributes().getIds().iterator().next();
394
395                 String JavaDoc pkName = id.getColumn() != null ? id.getColumn().getName() : id
396                         .getName();
397                 column.setName(relationship.getName() + '_' + pkName);
398                 column.setReferencedColumnName(id.getColumn() != null ? id
399                         .getColumn()
400                         .getName() : id.getName());
401             }
402
403             return true;
404         }
405     }
406
407     final class MappedSuperclassVisitor extends AbstractEntityVisitor {
408
409     }
410
411     final class FKVisitor extends RelationshipVisitor {
412
413         FKVisitor() {
414             addChildVisitor(JpaJoinColumn.class, new JoinColumnVisitor());
415         }
416
417         @Override JavaDoc
418         public boolean onStartNode(ProjectPath path) {
419
420             if (!super.onStartNode(path)) {
421                 return false;
422             }
423
424             Object JavaDoc relationship = path.getObject();
425             Collection JavaDoc<JpaJoinColumn> joinColumns = null;
426             String JavaDoc mappedBy = null;
427
428             if (relationship instanceof JpaOneToOne) {
429                 joinColumns = ((JpaOneToOne) relationship).getJoinColumns();
430                 mappedBy = ((JpaOneToOne) relationship).getMappedBy();
431                 if (joinColumns.isEmpty() && mappedBy != null) {
432                     joinColumns.add(new JpaJoinColumn(AnnotationPrototypes
433                             .getJoinColumn()));
434                 }
435             }
436             else if (relationship instanceof JpaOneToMany) {
437                 joinColumns = ((JpaOneToMany) relationship).getJoinColumns();
438                 mappedBy = ((JpaOneToMany) relationship).getMappedBy();
439                 if (joinColumns.isEmpty() && mappedBy != null) {
440                     joinColumns.add(new JpaJoinColumn(AnnotationPrototypes
441                             .getJoinColumn()));
442                 }
443             }
444             else if (relationship instanceof JpaManyToOne) {
445                 joinColumns = ((JpaManyToOne) relationship).getJoinColumns();
446                 if (joinColumns.isEmpty()) {
447                     joinColumns.add(new JpaJoinColumn(AnnotationPrototypes
448                             .getJoinColumn()));
449                 }
450             }
451
452             return true;
453         }
454     }
455
456     class RelationshipVisitor extends BaseTreeVisitor {
457
458         @Override JavaDoc
459         public boolean onStartNode(ProjectPath path) {
460
461             JpaRelationship relationship = (JpaRelationship) path.getObject();
462             if (Util.isEmptyString(relationship.getTargetEntityName())) {
463
464                 JpaEntity entity = (JpaEntity) path.firstInstanceOf(JpaEntity.class);
465
466                 String JavaDoc name = relationship.getName();
467
468                 JpaClassDescriptor srcDescriptor = entity.getClassDescriptor();
469                 JpaPropertyDescriptor property = srcDescriptor.getProperty(name);
470
471                 Class JavaDoc targetEntityType = property.getTargetEntityType();
472
473                 if (targetEntityType == null) {
474                     context.recordConflict(new SimpleValidationFailure(property
475                             .getMember(), "Undefined target entity type: " + name));
476                     return false;
477                 }
478                 else {
479                     relationship.setTargetEntityName(targetEntityType.getName());
480                 }
481             }
482
483             return true;
484         }
485     }
486 }
487
Popular Tags