KickJava   Java API By Example, From Geeks To Geeks.

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


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.lang.annotation.Annotation JavaDoc;
23 import java.lang.reflect.AnnotatedElement JavaDoc;
24 import java.lang.reflect.Method JavaDoc;
25 import java.lang.reflect.Modifier JavaDoc;
26 import java.util.ArrayList JavaDoc;
27 import java.util.Arrays JavaDoc;
28 import java.util.Collection JavaDoc;
29 import java.util.Comparator JavaDoc;
30 import java.util.HashMap JavaDoc;
31 import java.util.LinkedList JavaDoc;
32 import java.util.Map JavaDoc;
33
34 import javax.persistence.AssociationOverride;
35 import javax.persistence.AssociationOverrides;
36 import javax.persistence.AttributeOverride;
37 import javax.persistence.AttributeOverrides;
38 import javax.persistence.Basic;
39 import javax.persistence.Column;
40 import javax.persistence.Embeddable;
41 import javax.persistence.Embedded;
42 import javax.persistence.EmbeddedId;
43 import javax.persistence.Entity;
44 import javax.persistence.EntityListeners;
45 import javax.persistence.Enumerated;
46 import javax.persistence.GeneratedValue;
47 import javax.persistence.Id;
48 import javax.persistence.Lob;
49 import javax.persistence.ManyToMany;
50 import javax.persistence.ManyToOne;
51 import javax.persistence.MapKey;
52 import javax.persistence.MappedSuperclass;
53 import javax.persistence.NamedNativeQueries;
54 import javax.persistence.NamedNativeQuery;
55 import javax.persistence.NamedQueries;
56 import javax.persistence.NamedQuery;
57 import javax.persistence.OneToMany;
58 import javax.persistence.OneToOne;
59 import javax.persistence.OrderBy;
60 import javax.persistence.SequenceGenerator;
61 import javax.persistence.SqlResultSetMapping;
62 import javax.persistence.TableGenerator;
63 import javax.persistence.Temporal;
64 import javax.persistence.Transient;
65 import javax.persistence.Version;
66
67 import org.apache.cayenne.jpa.JpaProviderException;
68 import org.apache.cayenne.jpa.map.AccessType;
69 import org.apache.cayenne.jpa.map.JpaAbstractEntity;
70 import org.apache.cayenne.jpa.map.JpaAttribute;
71 import org.apache.cayenne.jpa.map.JpaClassDescriptor;
72 import org.apache.cayenne.jpa.map.JpaManagedClass;
73 import org.apache.cayenne.jpa.map.JpaPropertyDescriptor;
74 import org.apache.cayenne.util.Util;
75 import org.apache.cayenne.validation.SimpleValidationFailure;
76
77 /**
78  * {@link org.apache.cayenne.jpa.map.JpaEntityMap} loader that reads mapping information
79  * from the class annotations per JPA specification.
80  * <h3>Specification Documentation, persistence_1_0.xsd, "class" element.</h3>
81  * <p>
82  * [Each managed class] should be annotated with either \@Entity, \@Embeddable or
83  * \@MappedSuperclass
84  *
85  * @author Andrus Adamchik
86  */

87 public class EntityMapAnnotationLoader {
88
89     static final Map JavaDoc<String JavaDoc, Integer JavaDoc> TYPE_ANNOTATION_ORDERING_WEIGHTS;
90     static final Map JavaDoc<String JavaDoc, Integer JavaDoc> MEMBER_ANNOTATION_ORDERING_WEIGHTS;
91
92     static {
93
94         TYPE_ANNOTATION_ORDERING_WEIGHTS = new HashMap JavaDoc<String JavaDoc, Integer JavaDoc>();
95
96         // annotations that are top-level only
97
TYPE_ANNOTATION_ORDERING_WEIGHTS.put(Entity.class.getName(), 1);
98         TYPE_ANNOTATION_ORDERING_WEIGHTS.put(Embeddable.class.getName(), 1);
99         TYPE_ANNOTATION_ORDERING_WEIGHTS.put(MappedSuperclass.class.getName(), 1);
100
101         // annotations that can be a part of Entity or EntityMap
102
TYPE_ANNOTATION_ORDERING_WEIGHTS.put(SequenceGenerator.class.getName(), 2);
103         TYPE_ANNOTATION_ORDERING_WEIGHTS.put(NamedNativeQueries.class.getName(), 2);
104         TYPE_ANNOTATION_ORDERING_WEIGHTS.put(NamedNativeQuery.class.getName(), 2);
105         TYPE_ANNOTATION_ORDERING_WEIGHTS.put(NamedQueries.class.getName(), 2);
106         TYPE_ANNOTATION_ORDERING_WEIGHTS.put(NamedQuery.class.getName(), 2);
107         TYPE_ANNOTATION_ORDERING_WEIGHTS.put(SqlResultSetMapping.class.getName(), 2);
108         TYPE_ANNOTATION_ORDERING_WEIGHTS.put(TableGenerator.class.getName(), 2);
109         TYPE_ANNOTATION_ORDERING_WEIGHTS.put(EntityListeners.class.getName(), 2);
110
111         MEMBER_ANNOTATION_ORDERING_WEIGHTS = new HashMap JavaDoc<String JavaDoc, Integer JavaDoc>();
112
113         // first level of member annotations - annotations representing different types of
114
// attributes
115
MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Id.class.getName(), 1);
116         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Basic.class.getName(), 1);
117         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(EmbeddedId.class.getName(), 1);
118         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Version.class.getName(), 1);
119         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(ManyToOne.class.getName(), 1);
120         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(OneToMany.class.getName(), 1);
121         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(OneToOne.class.getName(), 1);
122         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(ManyToMany.class.getName(), 1);
123         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Embedded.class.getName(), 1);
124         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Transient.class.getName(), 1);
125         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(AssociationOverride.class.getName(), 1);
126         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(AssociationOverrides.class.getName(), 1);
127
128         // second level - attribute overrides (can belong to Embedded or can be a part of
129
// the entity )
130
MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(AttributeOverride.class.getName(), 2);
131         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(AttributeOverrides.class.getName(), 2);
132
133         // third level of member annotations - details implying one of the attributes
134
// above
135
MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(GeneratedValue.class.getName(), 3);
136         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Temporal.class.getName(), 3);
137         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(TableGenerator.class.getName(), 3);
138         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(SequenceGenerator.class.getName(), 3);
139
140         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Lob.class.getName(), 3);
141         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Temporal.class.getName(), 3);
142         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Enumerated.class.getName(), 3);
143
144         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(MapKey.class.getName(), 3);
145         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(OrderBy.class.getName(), 3);
146         MEMBER_ANNOTATION_ORDERING_WEIGHTS.put(Column.class.getName(), 3);
147     }
148
149     protected EntityMapLoaderContext context;
150
151     protected Comparator JavaDoc<Annotation JavaDoc> typeAnnotationsSorter;
152     protected Comparator JavaDoc<Annotation JavaDoc> memberAnnotationsSorter;
153
154     protected AnnotationProcessorFactory classProcessorFactory;
155     protected AnnotationProcessorFactory memberProcessorFactory;
156     protected AnnotationProcessorFactory callbackProcessorFactory;
157
158     public EntityMapAnnotationLoader(EntityMapLoaderContext context) {
159         this.context = context;
160         this.typeAnnotationsSorter = new AnnotationSorter(
161                 TYPE_ANNOTATION_ORDERING_WEIGHTS);
162         this.memberAnnotationsSorter = new AnnotationSorter(
163                 MEMBER_ANNOTATION_ORDERING_WEIGHTS);
164
165         this.classProcessorFactory = new ClassAnnotationProcessorFactory();
166         this.memberProcessorFactory = new MemberAnnotationProcessorFactory();
167         this.callbackProcessorFactory = new EntityCallbackAnnotationProcessorFactory();
168     }
169
170     /**
171      * Processes annotations of a single Java class, loading ORM mapping information to
172      * the provided entity map.
173      */

174     public void loadClassMapping(Class JavaDoc managedClass) throws JpaProviderException {
175
176         // avoid duplicates loaded from annotations per CAY-756
177
if (context.getEntityMap().containsManagedClass(managedClass.getName())) {
178             context.recordConflict(new SimpleValidationFailure(
179                     managedClass.getName(),
180                     "Duplicate managed class declaration " + managedClass.getName()));
181             return;
182         }
183
184         Annotation JavaDoc[] classAnnotations = managedClass.getAnnotations();
185
186         // per 'getAnnotations' docs, array is returned by copy, so we can modify it...
187
Arrays.sort(classAnnotations, typeAnnotationsSorter);
188
189         JpaClassDescriptor descriptor = new JpaClassDescriptor(managedClass);
190
191         // initially set access to the map level access - may be overriden below
192
descriptor.setAccess(context.getEntityMap().getAccess());
193
194         AnnotationContext stack = new AnnotationContext(descriptor);
195         stack.push(context.getEntityMap());
196
197         // === push class-level stuff
198
for (int i = 0; i < classAnnotations.length; i++) {
199             AnnotationProcessor processor = classProcessorFactory
200                     .getProcessor(classAnnotations[i]);
201             if (processor != null) {
202                 processor.onStartElement(managedClass, stack);
203             }
204         }
205
206         // if class is not properly annotated, bail early
207
if (stack.depth() == 1) {
208             return;
209         }
210
211         // apply entity callbacks ...
212
if (stack.peek() instanceof JpaAbstractEntity) {
213             for (Method JavaDoc callback : getEntityCallbacks(managedClass)) {
214                 applyEntityCallbackAnnotations(callback, stack);
215             }
216         }
217
218         // per JPA spec, 2.1.1, regarding access type:
219

220         // When annotations are used, the placement of the mapping annotations on either
221
// the persistent fields or persistent properties of the entity class specifies
222
// the access type as being either field- or property-based access respectively.
223

224         // Question (andrus) - if no annotations are placed at the field or method level,
225
// we still must determine the access type to apply default mappping rules. How?
226
// (using FIELD access for now).
227

228         boolean fieldAccess = false;
229
230         for (JpaPropertyDescriptor property : descriptor.getFieldDescriptors()) {
231             stack.setPropertyDescriptor(property);
232             if (applyMemberAnnotations(property, stack)) {
233                 fieldAccess = true;
234             }
235         }
236
237         boolean propertyAccess = false;
238
239         for (JpaPropertyDescriptor property : descriptor.getPropertyDescriptors()) {
240             stack.setPropertyDescriptor(property);
241             if (applyMemberAnnotations(property, stack)) {
242                 propertyAccess = true;
243             }
244         }
245
246         if (stack.peek() instanceof JpaManagedClass) {
247             JpaManagedClass entity = (JpaManagedClass) stack.peek();
248             // sanity check
249
if (fieldAccess && propertyAccess) {
250                 throw new JpaProviderException("Entity '"
251                         + entity.getClassName()
252                         + "' has both property and field annotations.");
253             }
254
255             // TODO: andrus - 11/29/2006 - clean this redundancy - access field should be
256
// stored either in the entity or the descriptor.
257
if (fieldAccess) {
258                 descriptor.setAccess(AccessType.FIELD);
259                 entity.setAccess(AccessType.FIELD);
260             }
261             else if (propertyAccess) {
262                 descriptor.setAccess(AccessType.PROPERTY);
263                 entity.setAccess(AccessType.PROPERTY);
264             }
265         }
266
267         // === pop class-level stuff
268
for (int i = classAnnotations.length - 1; i >= 0; i--) {
269             AnnotationProcessor processor = classProcessorFactory
270                     .getProcessor(classAnnotations[i]);
271             if (processor != null) {
272                 processor.onFinishElement(managedClass, stack);
273             }
274         }
275     }
276
277     /**
278      * Processes member annotations, returning true if at least one JPA annotation was
279      * found.
280      */

281     protected boolean applyMemberAnnotations(
282             JpaPropertyDescriptor property,
283             AnnotationProcessorStack stack) {
284
285         AnnotatedElement JavaDoc member = property.getMember();
286
287         Annotation JavaDoc[] annotations = member.getAnnotations();
288         // per 'getAnnotations' docs, array is returned by copy, so we can modify it...
289
Arrays.sort(annotations, memberAnnotationsSorter);
290
291         for (int j = 0; j < annotations.length; j++) {
292
293             AnnotationProcessor memberProcessor = memberProcessorFactory
294                     .getProcessor(annotations[j]);
295
296             if (memberProcessor != null) {
297                 memberProcessor.onStartElement(member, stack);
298             }
299         }
300
301         for (int j = annotations.length - 1; j >= 0; j--) {
302
303             AnnotationProcessor memberProcessor = memberProcessorFactory
304                     .getProcessor(annotations[j]);
305
306             if (memberProcessor != null) {
307                 memberProcessor.onFinishElement(member, stack);
308             }
309         }
310
311         return annotations.length > 0;
312     }
313
314     protected void applyEntityCallbackAnnotations(
315             Method JavaDoc method,
316             AnnotationProcessorStack stack) {
317
318         Annotation JavaDoc[] annotations = method.getAnnotations();
319
320         for (int j = 0; j < annotations.length; j++) {
321
322             AnnotationProcessor callbackProcessor = callbackProcessorFactory
323                     .getProcessor(annotations[j]);
324
325             if (callbackProcessor != null) {
326                 callbackProcessor.onStartElement(method, stack);
327             }
328         }
329
330         // don't call 'onFinishElement' as there is no nesting within callback
331
// annotations...
332
}
333
334     /**
335      * Returns a collection of methods that match an 'entity callback" pattern, i.e. "void
336      * <METHOD>()".
337      */

338     protected Collection JavaDoc<Method JavaDoc> getEntityCallbacks(Class JavaDoc managedClass) {
339
340         Collection JavaDoc<Method JavaDoc> callbacks = new ArrayList JavaDoc<Method JavaDoc>(3);
341
342         Method JavaDoc[] methods = managedClass.getDeclaredMethods();
343         for (int i = 0; i < methods.length; i++) {
344
345             int modifiers = methods[i].getModifiers();
346             if (Modifier.isStatic(modifiers) || Modifier.isFinal(modifiers)) {
347                 continue;
348             }
349
350             if (!Void.TYPE.equals(methods[i].getReturnType())) {
351                 continue;
352             }
353
354             Class JavaDoc[] params = methods[i].getParameterTypes();
355             if (params.length != 0) {
356                 continue;
357             }
358
359             callbacks.add(methods[i]);
360         }
361
362         return callbacks;
363     }
364
365     /**
366      * Comparator for TYPE level JPA annotations that first returns top-level annotations
367      * that define what kind of managed persistent class is being annotated.
368      */

369     final class AnnotationSorter implements Comparator JavaDoc<Annotation JavaDoc> {
370
371         private Map JavaDoc<String JavaDoc, Integer JavaDoc> weights;
372
373         AnnotationSorter(Map JavaDoc<String JavaDoc, Integer JavaDoc> weights) {
374             this.weights = weights;
375         }
376
377         public int compare(Annotation JavaDoc o1, Annotation JavaDoc o2) {
378             Integer JavaDoc w1 = weights.get(o1.annotationType().getName());
379             Integer JavaDoc w2 = weights.get(o2.annotationType().getName());
380
381             // nulls go last as all non-top annotations are not explicitly mentioned
382
// mapped to sorting weight
383
return Util.nullSafeCompare(false, w1, w2);
384         }
385     }
386
387     final class AnnotationContext implements AnnotationProcessorStack {
388
389         LinkedList JavaDoc stack = new LinkedList JavaDoc();
390         JpaClassDescriptor classDescriptor;
391         JpaPropertyDescriptor propertyDescriptor;
392
393         AnnotationContext(JpaClassDescriptor classDescriptor) {
394             this.classDescriptor = classDescriptor;
395         }
396
397         void setPropertyDescriptor(JpaPropertyDescriptor propertyDescriptor) {
398             this.propertyDescriptor = propertyDescriptor;
399         }
400
401         public int depth() {
402             return stack.size();
403         }
404
405         public Object JavaDoc peek() {
406             return stack.peek();
407         }
408
409         public Object JavaDoc pop() {
410             return stack.removeFirst();
411         }
412
413         public void push(Object JavaDoc object) {
414
415             // do descriptor injection...
416
if (object instanceof JpaAttribute) {
417                 JpaAttribute attribute = (JpaAttribute) object;
418                 attribute.setName(propertyDescriptor.getName());
419                 attribute.setPropertyDescriptor(propertyDescriptor);
420             }
421             else if (object instanceof JpaManagedClass) {
422                 ((JpaManagedClass) object).setClassDescriptor(classDescriptor);
423             }
424
425             stack.addFirst(object);
426         }
427
428         public void recordConflict(
429                 AnnotatedElement JavaDoc element,
430                 Class JavaDoc annotatedType,
431                 String JavaDoc message) {
432
433             StringBuilder JavaDoc buffer = new StringBuilder JavaDoc();
434             buffer.append("Problem processing annotation: ").append(
435                     annotatedType.getName());
436             buffer.append(", annotated element: ").append(element);
437
438             if (message != null) {
439                 buffer.append(", details: ").append(message);
440             }
441
442             context
443                     .recordConflict(new SimpleValidationFailure(peek(), buffer.toString()));
444         }
445     }
446 }
447
Popular Tags