View Javadoc

1   /*
2    * Copyright 2005 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS, 
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License.
15   */
16  
17  package org.apache.jdo.impl.model.jdo;
18  
19  import org.apache.jdo.model.ModelException;
20  import org.apache.jdo.model.java.JavaType;
21  import org.apache.jdo.model.jdo.JDOClass;
22  import org.apache.jdo.model.jdo.JDOField;
23  import org.apache.jdo.model.jdo.JDORelationship;
24  
25  /***
26   * JDORelationship is the super interface for all interfaces representing 
27   * JDO relationship metadata of a managed field of a persistence capable class.
28   * 
29   * @author Michael Bouschen
30   * @version 2.0
31   */
32  public abstract class JDORelationshipImpl extends JDOElementImpl
33      implements JDORelationship {
34      
35      /*** Property lowerBound. No default. */
36      private int lowerBound;
37  
38      /*** Property upperBound. No default. */
39      private int upperBound;
40  
41      /*** Relationship JDOField<->JDORelationship. */
42      private JDOField declaringField;
43  
44      /*** Relationship JDORelationship<->JDORelationship. */
45      protected JDORelationship mappedBy;
46  
47      /*** Name of the field which is the inverse relationship */
48      private String inverseName;
49  
50      /*** Relationship JDORelationship<->JDORelationship. */
51      protected JDORelationship inverse;
52  
53      /*** 
54       * Get the lower cardinality bound for this relationship element.
55       * @return the lower cardinality bound
56       */
57      public int getLowerBound() {
58          return lowerBound;
59      }
60  
61      /*** 
62       * Set the lower cardinality bound for this relationship element.
63       * @param lowerBound an integer indicating the lower cardinality bound
64       */
65      public void setLowerBound(int lowerBound) {
66          this.lowerBound = lowerBound;
67      }
68      
69      /*** 
70       * Get the upper cardinality bound for this relationship element.
71       * @return the upper cardinality bound
72       */
73      public int getUpperBound() {
74          return upperBound;
75      }
76  
77      /*** 
78       * Set the upper cardinality bound for this relationship element.
79       * @param upperBound an integer indicating the upper cardinality bound
80       */
81      public void setUpperBound(int upperBound)
82      {
83          this.upperBound = upperBound;
84      }
85  
86      /*** 
87       * Get the declaring field of this JDORelationship.
88       * @return the field that owns this JDORelationship, or <code>null</code>
89       * if the element is not attached to any field
90       */
91      public JDOField getDeclaringField() {
92          return declaringField;
93      }
94  
95      /*** 
96       * Set the declaring field of this JDORelationship.
97       * @param declaringField the declaring field of this relationship element
98       */
99      public void setDeclaringField(JDOField declaringField) {
100         this.declaringField = declaringField;
101     }
102 
103     /***
104      * Get the JDOClass corresponding to the type or element of this 
105      * relationship.
106      * @return the related class
107      */
108     public JDOClass getRelatedJDOClass() {
109         JavaType relatedType = getRelatedJavaType();
110 
111         if (relatedType != null) {
112             JDOClass myClass = getDeclaringField().getDeclaringClass();
113             String relatedTypeName = relatedType.getName();
114 
115             if (relatedTypeName.equals(myClass.getName()))
116                 return myClass;
117         
118             return myClass.getDeclaringModel().getJDOClass(relatedTypeName);
119         }
120 
121         return null;
122     }
123 
124     /*** 
125      * Get the mappedBy relationship. If there is no mappedBy relationship
126      * set, the method checks the mappedBy name as specified in the declaring
127      * field and resolves the relationship. The method return
128      * <code>null</code> if there is no mappedBy relationship set and there
129      * is no mappedBy name specified on the declaring field.
130      * @return the mappedBy relationship if available; <code>null</code>
131      * otherwise.
132      */
133     public JDORelationship getMappedBy() {
134         if (mappedBy != null) {
135             // return mappedBy relationship, if explicitly set by the setter
136             return mappedBy;
137         }
138         
139         // not set => check mappedByName of declaring field
140         JDOField field = getDeclaringField();
141         String mappedByName = field.getMappedByName();
142         if (mappedByName != null) {
143             // return a JDORelationship instance for the mappedBy field name
144             // as stored in the declaring field
145             return getInverseRelationship();
146         }
147         
148         return null;
149     }
150 
151     /***
152      * Set the mappedBy relationship for this relationship. This method
153      * automatically updates the mappedBy name of the declaring field of this
154      * relationship.
155      * @param mappedBy the mappedBy relationship.
156      * @exception ModelException if impossible
157      */
158     public void setMappedBy(JDORelationship mappedBy) throws ModelException {
159         this.mappedBy = mappedBy;
160         String mappedByName = null;
161         if (mappedBy != null) {
162             JDOField declaringField = mappedBy.getDeclaringField();
163             if (declaringField != null) {
164                 mappedByName = declaringField.getName();
165             }
166         }
167         getDeclaringField().setMappedByName(mappedByName);
168         setInverseRelationship(mappedBy);
169     }
170 
171     /*** 
172      * Get the relative name of the inverse relationship field for this
173      * relationship.  In the case of two-way relationships, the two
174      * relationships involved are inverses of each other.  If this
175      * relationship element does not participate in a two-way relationship,
176      * this returns <code>null</code>.  Note that it is possible to have
177      * this method return a value, but because of the combination of
178      * related class and lookup, there may be no corresponding
179      * JDORelationship which can be found.
180      * @return the relative name of the inverse JDORelationship
181      * @see #getInverseRelationship
182      */
183     public String getInverseRelationshipName() {
184         if (inverseName != null) {
185             // return inverseName, if explicitly set
186             return inverseName;
187         }
188         
189         JDOField declaringField = getDeclaringField();
190         String mappedByName = declaringField.getMappedByName();
191         if (mappedByName != null) {
192             // return mappedByName, if explicitly set on the declaring field
193             return mappedByName;
194         }
195 
196         // try to resolve relationship info from mappedByName of the field on
197         // the other side
198         UnresolvedRelationshipHelper info = getUnresolvedRelationshipHelper();
199         // look for an unresolved relationship entry where the name of this
200         // field is used as the mappedByName
201         JDOField inverseField = 
202             info.resolve(declaringField.getName(), getRelatedJDOClass());
203         if (inverseField != null) {
204             // found inverse => update inverseName and return it
205             inverseName = inverseField.getName();
206             return inverseName;
207         }
208 
209         // no inverse name available => return null
210         return null;
211     }
212 
213     /***
214      * Get the inverse JDORelationship in the case of a two-way relationship.
215      * @return the inverse relationship
216      */
217     public JDORelationship getInverseRelationship() {
218         if (inverse != null) {
219             // return inverse relationship, if explicitly set by the setter
220             return inverse;
221         }
222 
223         // not set => check inverse name 
224         String fieldName = getInverseRelationshipName();
225         if (fieldName != null) {
226             JDOClass relatedClass = getRelatedJDOClass();
227             JDOField relatedField = relatedClass.getField(fieldName);
228             if (relatedField != null)
229                 return relatedField.getRelationship();
230         }
231         return null;
232     }
233 
234     /***
235      * Set the inverse JDORelationship in the case of a two-way relationship.
236      * The two relationship elements involved are set as inverses of each 
237      * other and the old inverse is unset.
238      * <p>
239      * Warning: this methods casts the existing and the specified inverse
240      * relationship instance to JDORelationshipImpl.
241      * @param inverseRelationship the inverse relationship
242      */
243     public void setInverseRelationship(JDORelationship inverseRelationship) 
244         throws ModelException {
245         
246         // Skip setting the inverse, if already it is set to the specified
247         // instance. Note, do not use the result of getInverseRelationship for
248         // this check, since it might calculate an inverse relationship
249         // instance that is identical to the specified one, although the
250         // inverse is not yet set.
251         if (this.inverse == inverseRelationship) {
252             return;
253         }
254 
255         // clear old inverse which still points to here
256         JDORelationshipImpl old = 
257             (JDORelationshipImpl) getInverseRelationship();
258         if (old != null) {
259             if (this.equals(old.getInverseRelationship()))
260                 old.changeInverseRelationship(null);
261         }
262 
263         // link from here to new inverse
264         changeInverseRelationship(inverseRelationship);
265 
266         // link from new inverse back to here
267         if (inverseRelationship != null) {
268             ((JDORelationshipImpl) inverseRelationship).
269                 changeInverseRelationship(this);
270         }
271     }
272 
273     /***
274      * Determines whether this side of a two-way relationship is the
275      * owning side.
276      * @return <code>true</code> if this side is the owning side;
277      * <code>false</code> otherwise. 
278      */
279     public boolean isOwner() {
280         return getMappedBy() == null;
281     }
282 
283     /***
284      * Determines whether this JDORelationship represents a reference
285      * relationship or not. A return of <code>true</code> means this
286      * JDORelationship is a JDOReference instance.
287      * @return <code>true</code> if this JDORelationship represents a
288      * reference relationship; <code>false</code> otherwise.
289      */
290     public boolean isJDOReference() {
291         return false;
292     }
293     
294     /***
295      * Determines whether this JDORelationship represents a collection
296      * relationship or not. A return of <code>true</code> means this
297      * JDORelationship is a JDOCollection instance.
298      * @return <code>true</code> if this JDORelationship represents a
299      * collection relationship; <code>false</code> otherwise.
300      */
301     public boolean isJDOCollection() {
302         return false;
303     }
304 
305     /***
306      * Determines whether this JDORelationship represents an array
307      * relationship or not. A return of <code>true</code> means this
308      * JDORelationship is a JDOArray instance.
309      * @return <code>true</code> if this JDORelationship represents an 
310      * array relationship; <code>false</code> otherwise.
311      */
312     public boolean isJDOArray() {
313         return false;
314     }
315 
316     /***
317      * Determines whether this JDORelationship represents a map 
318      * relationship or not. A return of <code>true</code> means this
319      * JDORelationship is a JDOMap instance.
320      * @return <code>true</code> if this JDORelationship represents a
321      * map relationship; <code>false</code> otherwise.
322      */
323     public boolean isJDOMap() {
324         return false;
325     }
326 
327     //========= Internal helper methods ==========
328 
329     /*** 
330      * Get the type representation of the relationship. This will be 
331      * the JavaType for references, the element type for collections
332      * and arrays, and the value type for maps.
333      * @return the relationship type
334      */
335     public abstract JavaType getRelatedJavaType();
336 
337     /*** Changes the inverse relationship element for this relationship 
338      * element.
339      * This method is invoked for both sides from
340      * {@link #setInverseRelationship} and should handle setting the 
341      * internal variable.
342      * @param inverseRelationship - a relationship element to be used as the
343      * inverse for this relationship element or <code>null</code> if this
344      * relationship element does not participate in a two-way relationship.
345      * @exception ModelException if impossible
346      */
347     private void changeInverseRelationship(JDORelationship 
348         inverseRelationship) throws ModelException {
349         this.inverse = inverseRelationship;
350         this.inverseName = ((inverseRelationship == null) ? null :
351             inverseRelationship.getDeclaringField().getName());
352     }
353 
354     /*** 
355      * Returns the UnresolvedRelationshipHelper instance from the declaring
356      * field.
357      * @return the current UnresolvedRelationshipHelper
358      */
359     private UnresolvedRelationshipHelper getUnresolvedRelationshipHelper() {
360         return ((JDOFieldImplDynamic) getDeclaringField()).
361             getUnresolvedRelationshipHelper();
362     }    
363 }