View Javadoc

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