KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > oracle > toplink > essentials > internal > ejb > cmp3 > metadata > accessors > CollectionAccessor


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the "License"). You may not use this file except
5  * in compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * glassfish/bootstrap/legal/CDDLv1.0.txt or
9  * https://glassfish.dev.java.net/public/CDDLv1.0.html.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * HEADER in each file and include the License file at
15  * glassfish/bootstrap/legal/CDDLv1.0.txt. If applicable,
16  * add the following below this CDDL HEADER, with the
17  * fields enclosed by brackets "[]" replaced with your
18  * own identifying information: Portions Copyright [yyyy]
19  * [name of copyright owner]
20  */

21 // Copyright (c) 1998, 2006, Oracle. All rights reserved.
22
package oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors;
23
24 import java.util.Collection JavaDoc;
25 import java.util.Map JavaDoc;
26 import java.util.List JavaDoc;
27 import java.util.Set JavaDoc;
28 import java.util.StringTokenizer JavaDoc;
29
30 import javax.persistence.MapKey;
31 import javax.persistence.OrderBy;
32 import javax.persistence.JoinTable;
33
34 import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataLogger;
35 import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataHelper;
36 import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataConstants;
37 import oracle.toplink.essentials.internal.ejb.cmp3.metadata.MetadataDescriptor;
38
39 import oracle.toplink.essentials.internal.ejb.cmp3.metadata.columns.MetadataJoinColumn;
40 import oracle.toplink.essentials.internal.ejb.cmp3.metadata.columns.MetadataJoinColumns;
41
42 import oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.ClassAccessor;
43 import oracle.toplink.essentials.internal.ejb.cmp3.metadata.accessors.objects.MetadataAccessibleObject;
44
45 import oracle.toplink.essentials.internal.ejb.cmp3.metadata.tables.MetadataJoinTable;
46
47 import oracle.toplink.essentials.internal.helper.ClassConstants;
48 import oracle.toplink.essentials.internal.helper.Helper;
49 import oracle.toplink.essentials.internal.helper.DatabaseField;
50
51 import oracle.toplink.essentials.internal.queryframework.CollectionContainerPolicy;
52
53 import oracle.toplink.essentials.mappings.CollectionMapping;
54 import oracle.toplink.essentials.mappings.ManyToManyMapping;
55
56 /**
57  * An annotation defined relational collections accessor.
58  *
59  * @author Guy Pelletier
60  * @since TopLink EJB 3.0 Reference Implementation
61  */

62 public abstract class CollectionAccessor extends RelationshipAccessor {
63     /**
64      * INTERNAL:
65      */

66     public CollectionAccessor(MetadataAccessibleObject accessibleObject, ClassAccessor classAccessor) {
67         super(accessibleObject, classAccessor);
68     }
69     
70     /**
71      * INTERNAL:
72      * Add the relation key fields to a many to many mapping.
73      */

74     protected void addManyToManyRelationKeyFields(MetadataJoinColumns joinColumns, ManyToManyMapping mapping, String JavaDoc defaultFieldName, MetadataDescriptor descriptor, boolean isSource) {
75         // Set the right context level.
76
String JavaDoc PK_CTX, FK_CTX;
77         if (isSource) {
78             PK_CTX = MetadataLogger.SOURCE_PK_COLUMN;
79             FK_CTX = MetadataLogger.SOURCE_FK_COLUMN;
80         } else {
81             PK_CTX = MetadataLogger.TARGET_PK_COLUMN;
82             FK_CTX = MetadataLogger.TARGET_FK_COLUMN;
83         }
84         
85         for (MetadataJoinColumn joinColumn : processJoinColumns(joinColumns, descriptor)) {
86             // If the pk field (referencedColumnName) is not specified, it
87
// defaults to the primary key of the referenced table.
88
String JavaDoc defaultPKFieldName = descriptor.getPrimaryKeyFieldName();
89             DatabaseField pkField = joinColumn.getPrimaryKeyField();
90             pkField.setName(getName(pkField, defaultPKFieldName, PK_CTX));
91             pkField.setTableName(descriptor.getPrimaryTableName());
92             
93             // If the fk field (name) is not specified, it defaults to the
94
// name of the referencing relationship property or field of the
95
// referencing entity + "_" + the name of the referenced primary
96
// key column. If there is no such referencing relationship
97
// property or field in the entity (i.e., a join table is used),
98
// the join column name is formed as the concatenation of the
99
// following: the name of the entity + "_" + the name of the
100
// referenced primary key column.
101
DatabaseField fkField = joinColumn.getForeignKeyField();
102             String JavaDoc defaultFKFieldName = defaultFieldName + "_" + defaultPKFieldName;
103             fkField.setName(getName(fkField, defaultFKFieldName, FK_CTX));
104             // Target table name here is the join table name.
105
// If the user had specified a different table name in the join
106
// column, it is igored. Perhaps an error or warning should be
107
// fired off.
108
fkField.setTableName(mapping.getRelationTableQualifiedName());
109             
110             // Add a target relation key to the mapping.
111
if (isSource) {
112                 mapping.addSourceRelationKeyField(fkField, pkField);
113             } else {
114                 mapping.addTargetRelationKeyField(fkField, pkField);
115             }
116         }
117     }
118     
119     /**
120      * INTERNAL: (Overridden in XMLManyToManyAccessor and XMLOneToManyAccessor)
121      * Process a @JoinTable.
122      */

123     protected MetadataJoinTable getJoinTable() {
124         JoinTable joinTable = getAnnotation(JoinTable.class);
125         return new MetadataJoinTable(joinTable);
126     }
127     
128     /**
129      * INTERNAL: (Overridden in XMLManyToManyAccessor and XMLOneToManyAccessor)
130      * Method to return a map key for a collection mapping. Assumes hasMapKey()
131      * has been called before asking for the map key name.
132      */

133     public String JavaDoc getMapKey() {
134         if (isAnnotationPresent(MapKey.class)) {
135             MapKey mapKey = getAnnotation(MapKey.class);
136             return mapKey.name();
137         } else {
138             return "";
139         }
140     }
141     
142     /**
143      * INTERNAL: (Overridden in XMLManyToManyAccessor and XMLOneToManyAccessor)
144      * Return the order by value on this accessor. Assumes hasOrderBy() has been
145      * called before asking for the order by value.
146      */

147     public String JavaDoc getOrderBy() {
148         OrderBy orderBy = getAnnotation(OrderBy.class);
149         return orderBy.value();
150     }
151     
152     /**
153      * INTERNAL: (Overridden in XMLManyToManyAccessor and XMLOneToManyAccessor)
154      * Method to check if this accessor has an @OrderBy.
155      */

156     public boolean hasOrderBy() {
157         return isAnnotationPresent(OrderBy.class);
158     }
159     
160     /**
161      * INTERNAL:
162      * Return true if this accessor uses a Map.
163      */

164     public boolean isMapCollectionAccessor() {
165         return getRawClass().equals(Map JavaDoc.class);
166     }
167     
168     /**
169      * INTERNAL:
170      */

171     protected void populateCollectionMapping(CollectionMapping mapping, String JavaDoc context) {
172         mapping.setIsReadOnly(false);
173         mapping.setIsPrivateOwned(false);
174         mapping.setAttributeName(getAttributeName());
175         
176         // Will check for PROPERTY access
177
setAccessorMethods(mapping);
178
179         // Process the cascade types.
180
processCascadeTypes(mapping);
181         
182         // Figure out the referenceClass (from targetEntity) and set on the mapping.
183
setReferenceClass(getTargetEntity(), context);
184         mapping.setReferenceClassName(getReferenceClassName());
185         
186         // Process an OrderBy id there is one.
187
processOrderBy(mapping);
188         
189         // Process a MapKey if there is one.
190
String JavaDoc mapKey = processMapKey(mapping);
191         
192         // Set the correct indirection on the collection mapping.
193
// ** Note the reference class or reference class name needs to be set
194
// on the mapping before setting the indirection policy.
195
setIndirectionPolicy(mapping, mapKey);
196     }
197     
198     /**
199      * INTERNAL:
200      * Process a MetadataJoinTable.
201      */

202     protected void processJoinTable(MetadataJoinTable joinTable, ManyToManyMapping mapping) {
203         // Build the default table name
204
String JavaDoc sourceName = Helper.getShortClassName(getJavaClassName()).toUpperCase();
205         String JavaDoc targetName = Helper.getShortClassName(getReferenceClassName()).toUpperCase();
206         String JavaDoc defaultName = sourceName + "_" + targetName;
207         
208         // Name could be "", need to check against the default name.
209
String JavaDoc name = getName(joinTable.getName(), defaultName, m_logger.JOIN_TABLE_NAME);
210         
211         // Catalog could be "", need to check for an XML default.
212
String JavaDoc catalog = getName(joinTable.getCatalog(), m_descriptor.getCatalog(), m_logger.JOIN_TABLE_CATALOG);
213         
214         // Schema could be "", need to check for an XML default.
215
String JavaDoc schema = getName(joinTable.getSchema(), m_descriptor.getSchema(), m_logger.JOIN_TABLE_SCHEMA);
216         
217         // Build a fully qualified name and set it on the table.
218
joinTable.setName(MetadataHelper.getFullyQualifiedTableName(name, catalog, schema));
219         
220         // Set the table on the mapping.
221
mapping.setRelationTable(joinTable.getDatabaseTable());
222         
223         // Add all the joinColumns (source foreign keys) to the mapping.
224
String JavaDoc defaultSourceFieldName;
225         if (getReferenceDescriptor().hasManyToManyAccessorFor(getJavaClassName())) {
226             defaultSourceFieldName = getReferenceDescriptor().getManyToManyAccessor(getJavaClassName()).getAttributeName();
227         } else {
228             defaultSourceFieldName = Helper.getShortClassName(getJavaClass().getName());
229         }
230         addManyToManyRelationKeyFields(joinTable.getJoinColumns(), mapping, defaultSourceFieldName, m_descriptor, true);
231         
232         // Add all the inverseJoinColumns (target foreign keys) to the mapping.
233
String JavaDoc defaultTargetFieldName = getAttributeName();
234         addManyToManyRelationKeyFields(joinTable.getInverseJoinColumns(), mapping, defaultTargetFieldName, getReferenceDescriptor(), false);
235     }
236     
237     /**
238      * INTERNAL:
239      * Process a MapKey for a 1-M or M-M mapping. Will return the map key
240      * method name that should be use, null otherwise.
241      */

242     protected String JavaDoc processMapKey(CollectionMapping mapping) {
243         String JavaDoc mapKey = null;
244         
245         if (isMapCollectionAccessor()) {
246             MetadataDescriptor referenceDescriptor = getReferenceDescriptor();
247             String JavaDoc mapKeyValue = getMapKey();
248             
249             if (mapKeyValue.equals("") && referenceDescriptor.hasCompositePrimaryKey()) {
250                 // No persistent property or field name has been provided, and
251
// the reference class has a composite primary key class. Let
252
// it fall through to return null for the map key. Internally,
253
// TopLink will use an instance of the composite primary key
254
// class as the map key.
255
} else {
256                 // A persistent property or field name may have have been
257
// provided. If one has not we will default to the primary
258
// key of the reference class. The primary key cannot be
259
// composite at this point.
260
String JavaDoc fieldOrPropertyName = getName(mapKeyValue, referenceDescriptor.getIdAttributeName(), getLogger().MAP_KEY_ATTRIBUTE_NAME);
261     
262                 // Look up the referenceAccessor
263
MetadataAccessor referenceAccessor = referenceDescriptor.getAccessorFor(fieldOrPropertyName);
264         
265                 if (referenceAccessor == null) {
266                     m_validator.throwCouldNotFindMapKey(fieldOrPropertyName, referenceDescriptor.getJavaClass(), mapping);
267                 }
268         
269                 mapKey = referenceAccessor.getName();
270             }
271         }
272         
273         return mapKey;
274     }
275     
276     /**
277      * INTERNAL:
278      * Process an order by value (if specified) for the given collection
279      * mapping. Order by specifies the ordering of the elements of a collection
280      * valued association at the point when the association is retrieved.
281      *
282      * The syntax of the value ordering element is an orderby_list, as follows:
283      *
284      * orderby_list ::= orderby_item [, orderby_item]*
285      * orderby_item ::= property_or_field_name [ASC | DESC]
286      *
287      * When ASC or DESC is not specified, ASC is assumed.
288      *
289      * If the ordering element is not specified, ordering by the primary key
290      * of the associated entity is assumed.
291      *
292      * The property or field name must correspond to that of a persistent
293      * property or field of the associated class. The properties or fields
294      * used in the ordering must correspond to columns for which comparison
295      * operators are supported.
296      */

297     protected void processOrderBy(CollectionMapping mapping) {
298         if (hasOrderBy()) {
299             String JavaDoc orderBy = getOrderBy();
300             MetadataDescriptor referenceDescriptor = getReferenceDescriptor();
301             
302             if (orderBy.equals("")) {
303                 // Default to the primary key field name(s).
304
List JavaDoc<String JavaDoc> orderByAttributes = referenceDescriptor.getIdOrderByAttributeNames();
305             
306                 if (referenceDescriptor.hasEmbeddedIdAttribute()) {
307                     String JavaDoc embeddedIdAttributeName = referenceDescriptor.getEmbeddedIdAttributeName();
308                 
309                     for (String JavaDoc orderByAttribute : orderByAttributes) {
310                         mapping.addAggregateOrderBy(embeddedIdAttributeName, orderByAttribute, false);
311                     }
312                 } else {
313                     for (String JavaDoc orderByAttribute : orderByAttributes) {
314                         mapping.addOrderBy(orderByAttribute, false);
315                     }
316                 }
317             } else {
318                 StringTokenizer JavaDoc commaTokenizer = new StringTokenizer JavaDoc(orderBy, ",");
319             
320                 while (commaTokenizer.hasMoreTokens()) {
321                     StringTokenizer JavaDoc spaceTokenizer = new StringTokenizer JavaDoc(commaTokenizer.nextToken());
322                     String JavaDoc propertyOrFieldName = spaceTokenizer.nextToken();
323                     MetadataAccessor referenceAccessor = referenceDescriptor.getAccessorFor(propertyOrFieldName);
324                 
325                     if (referenceAccessor == null) {
326                         m_validator.throwInvalidOrderByValue(getJavaClass(), propertyOrFieldName, referenceDescriptor.getJavaClass(), getName());
327                     }
328
329                     String JavaDoc attributeName = referenceAccessor.getAttributeName();
330                     String JavaDoc ordering = (spaceTokenizer.hasMoreTokens()) ? spaceTokenizer.nextToken() : MetadataConstants.ASCENDING;
331
332                     if (referenceAccessor.isEmbedded()) {
333                         for (String JavaDoc orderByAttributeName : referenceDescriptor.getOrderByAttributeNames()) {
334                             mapping.addAggregateOrderBy(attributeName, orderByAttributeName, ordering.equals(MetadataConstants.DESCENDING));
335                         }
336                     } else {
337                         mapping.addOrderBy(attributeName, ordering.equals(MetadataConstants.DESCENDING));
338                     }
339                 }
340             }
341         }
342     }
343     
344     /**
345      * INTERNAL:
346      * Set the correct indirection policy on the collection mapping. Method
347      * assume that the reference class has been set on the mapping before
348      * calling this method.
349      */

350     protected void setIndirectionPolicy(CollectionMapping mapping, String JavaDoc mapKey) {
351         Class JavaDoc rawClass = getRawClass();
352         
353         if (usesIndirection()) {
354             if (rawClass == Map JavaDoc.class) {
355                 mapping.useTransparentMap(mapKey);
356             } else if (rawClass == List JavaDoc.class) {
357                 mapping.useTransparentList();
358             } else if (rawClass == Collection JavaDoc.class) {
359                 mapping.useTransparentCollection();
360                 mapping.setContainerPolicy(new CollectionContainerPolicy(ClassConstants.IndirectList_Class));
361             } else if (rawClass == Set JavaDoc.class) {
362                 mapping.useTransparentSet();
363             } else {
364                 // Because of validation we should never get this far.
365
}
366         } else {
367             mapping.dontUseIndirection();
368             
369             if (rawClass == Map JavaDoc.class) {
370                 mapping.useMapClass(java.util.Hashtable JavaDoc.class, mapKey);
371             } else if (rawClass == Set JavaDoc.class) {
372                 mapping.useCollectionClass(java.util.HashSet JavaDoc.class);
373             } else {
374                 mapping.useCollectionClass(java.util.Vector JavaDoc.class);
375             }
376         }
377     }
378 }
379
Popular Tags