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 java.lang.reflect.Modifier;
20  import java.util.Arrays;
21  import java.util.List;
22  
23  import org.apache.jdo.impl.model.jdo.util.TypeSupport;
24  import org.apache.jdo.model.ModelException;
25  import org.apache.jdo.model.java.JavaField;
26  import org.apache.jdo.model.java.JavaType;
27  import org.apache.jdo.model.jdo.JDOArray;
28  import org.apache.jdo.model.jdo.JDOClass;
29  import org.apache.jdo.model.jdo.JDOCollection;
30  import org.apache.jdo.model.jdo.JDOField;
31  import org.apache.jdo.model.jdo.JDOMap;
32  import org.apache.jdo.model.jdo.JDOReference;
33  import org.apache.jdo.model.jdo.JDORelationship;
34  import org.apache.jdo.model.jdo.NullValueTreatment;
35  import org.apache.jdo.model.jdo.PersistenceModifier;
36  import org.apache.jdo.util.I18NHelper;
37  
38  /***
39   * An instance of this class represents the JDO metadata of a managed field 
40   * of a persistence capable class. This dynamic implementation only
41   * stores property values explicitly set by setter method. 
42   * <p>
43   * Please note, you cannot rely on the Java identity of the
44   * JDORelationship instance returned by {@link #getRelationship}.  
45   * The getter will always return a new Java Instance, unless the
46   * relationship is explicitly set by the setter 
47   * {@link #setRelationship(JDORelationship relationship)}.
48   * <p>
49   * TBD:
50   * <ul>
51   * <li> Change usage of POSSIBLY_PERSISTENT persistence-modifier as soon as 
52   * the enhancer fully supports it.
53   * <li> Property change support
54   * </ul> 
55   *
56   * @author Michael Bouschen
57   * @since 1.1
58   * @version 2.0
59   */
60  public class JDOFieldImplDynamic
61      extends JDOMemberImpl 
62      implements JDOField
63  {
64      /*** 
65       * Property persistenceModifier. 
66       * Default see {@link #getPersistenceModifier}. 
67       */
68      protected int persistenceModifier = PersistenceModifier.UNSPECIFIED;
69      
70      /*** Property primaryKey. Defaults to <code>false</code>. */
71      private boolean primaryKey = false;
72      
73      /*** Property nullValueTreatment. Defaults to none. */
74      private int nullValueTreatment = NullValueTreatment.NONE;
75  
76      /*** Property defaultFetchGroup. Default see {@link #isDefaultFetchGroup}. */
77      protected Boolean defaultFetchGroup;
78  
79      /*** Property embedded. Default see {@link #isEmbedded}. */
80      protected Boolean embedded;
81  
82      /*** Property javaField. No default. */
83      protected transient JavaField javaField;
84  
85      /*** Property serializable. Defaults to <code>false</code>. */
86      private boolean serializable = false;
87  
88      /*** Property mappedByName. Defaults to <code>null</code>. */
89      private String mappedByName = null;
90  
91      /*** Relationship JDOField<->JDORelationship. */
92      protected JDORelationship relationship;
93      
94      /*** I18N support */
95      protected final static I18NHelper msg =  
96          I18NHelper.getInstance(JDOFieldImplDynamic.class);
97  
98      /*** Constructor. */
99      protected JDOFieldImplDynamic(String name, JDOClass declaringClass) {
100         super(name, declaringClass);
101     }
102 
103     /***
104      * Get the persistence modifier of this JDOField.
105      * @return the persistence modifier, one of 
106      * {@link PersistenceModifier#NONE}, 
107      * {@link PersistenceModifier#PERSISTENT},
108      * {@link PersistenceModifier#TRANSACTIONAL}, or
109      * {@link PersistenceModifier#POSSIBLY_PERSISTENT}.
110      */
111     public int getPersistenceModifier() {
112         if (persistenceModifier != PersistenceModifier.UNSPECIFIED) {
113             // return persistenceModifier, if explicitly set by the setter
114             return persistenceModifier;
115         }
116         
117         // not set => calculate
118         int result = PersistenceModifier.UNSPECIFIED;
119         JavaType type = getType();
120         if (nameHasJDOPrefix()) {
121             result = PersistenceModifier.NONE;
122         }
123         else if (type != null) {
124             result = TypeSupport.isPersistenceFieldType(type) ?
125                 PersistenceModifier.POSSIBLY_PERSISTENT : 
126                 PersistenceModifier.NONE;
127         }
128 
129         return result;
130     }
131 
132     /*** 
133      * Set the persistence modifier for this JDOField.
134      * @param persistenceModifier an integer indicating the persistence 
135      * modifier, one of: {@link PersistenceModifier#UNSPECIFIED}, 
136      * {@link PersistenceModifier#NONE}, 
137      * {@link PersistenceModifier#PERSISTENT},
138      * {@link PersistenceModifier#TRANSACTIONAL}, or
139      * {@link PersistenceModifier#POSSIBLY_PERSISTENT}.
140      */
141     public void setPersistenceModifier (int persistenceModifier)
142         throws ModelException {
143         if (nameHasJDOPrefix() && 
144             (persistenceModifier == PersistenceModifier.PERSISTENT ||
145              persistenceModifier == PersistenceModifier.TRANSACTIONAL)) {
146             throw new ModelException(
147                 msg.msg("EXC_IllegalJDOPrefix", getName())); //NOI18N
148         }
149         this.persistenceModifier = persistenceModifier;
150     }
151     
152     /*** 
153      * Determines whether this JDOField is a key field or not.  
154      * @return <code>true</code> if the field is a key field, 
155      * <code>false</code> otherwise
156      */
157     public boolean isPrimaryKey() {
158         return primaryKey;
159     }
160 
161     /*** 
162      * Set whether this JDOField is a key field or not.
163      * @param primaryKey if <code>true</code>, the JDOField is marked 
164      * as a key field; otherwise, it is not
165      */
166     public void setPrimaryKey(boolean primaryKey) {
167         this.primaryKey = primaryKey;
168     }
169 
170     /***
171      * Gets the null value treatment indicator of this JDOField.
172      * @return the null value treatment of this JDOField, one of 
173      * {@link NullValueTreatment#NONE}, {@link NullValueTreatment#EXCEPTION} or
174      * {@link NullValueTreatment#DEFAULT}
175      */
176     public int getNullValueTreatment() {
177         return nullValueTreatment;
178     }
179 
180     /***
181      * Sets the null value treatment indicator for this JDOField.
182      * @param nullValueTreatment an integer indicating the null 
183      * value treatment, one of: {@link NullValueTreatment#NONE}, 
184      * {@link NullValueTreatment#EXCEPTION} or 
185      * {@link NullValueTreatment#DEFAULT}
186      */
187     public void setNullValueTreatment(int nullValueTreatment) {
188         this.nullValueTreatment = nullValueTreatment;
189     }
190 
191     /***
192      * Determines whether this JDOField is part of the default fetch group or 
193      * not.
194      * @return <code>true</code> if the field is part of the default fetch 
195      * group, <code>false</code> otherwise
196      */
197     public boolean isDefaultFetchGroup() {
198         if (defaultFetchGroup != null) {
199             // return dfg, if explicitly set by the setter
200             return defaultFetchGroup.booleanValue();
201         }
202         
203         // not set => calculate
204         boolean dfg = false;
205         if (isPrimaryKey()) {
206             dfg = false;
207         }
208         else {
209             JavaType type = getType();
210             if ((type != null) && type.isValue()) {
211                 dfg = true;
212             }
213         }
214         
215         return dfg;
216     }
217 
218     /***
219      * Set whether this JDOField is part of the default fetch group or not.
220      * @param defaultFetchGroup if <code>true</code>, the JDOField is marked  
221      * as beeing part of the default fetch group; otherwise, it is not
222      */
223     public void setDefaultFetchGroup(boolean defaultFetchGroup) {
224         this.defaultFetchGroup = 
225             defaultFetchGroup ? Boolean.TRUE : Boolean.FALSE;
226     }
227 
228     /***
229      * Determines whether the field should be stored if possible as part of
230      * the instance instead of as its own instance in the datastore.
231      * @return <code>true</code> if the field is stored as part of the instance;
232      * <code>false</code> otherwise
233      */
234     public boolean isEmbedded() {
235         if (embedded != null) {
236             // return embedded, if explicitly set by the setter
237             return embedded.booleanValue();
238         }
239         
240         // not set => calculate
241         boolean result = false;
242         JavaType type = getType();
243         if (type != null) {
244             result = TypeSupport.isEmbeddedFieldType(type);
245         }
246         return result;
247     }
248 
249     /***
250      * Set whether the field should be stored if possible as part of
251      * the instance instead of as its own instance in the datastore.
252      * @param embedded <code>true</code> if the field is stored as part of the 
253      * instance; <code>false</code> otherwise
254      */
255     public void setEmbedded(boolean embedded) {
256         this.embedded = (embedded ? Boolean.TRUE : Boolean.FALSE);
257     }
258     
259     /***
260      * Get the corresponding JavaField representation for this JDOField.
261      * @return the corresponding JavaField representation
262      */
263     public JavaField getJavaField() {
264         if (javaField != null) {
265             // return java field, if explicitly set by the setter
266             return javaField;
267         }
268         
269         // not set => calculate
270         JavaType javaType = getDeclaringClass().getJavaType();
271         return javaType.getJavaField(getName());
272     }
273 
274     /***
275      * Sets the corresponding Java field representation for this JDOField.
276      * @param javaField the corresponding Java field representation
277      */
278     public void setJavaField (JavaField javaField) throws ModelException {
279         this.javaField = javaField;
280     }
281     
282     /***
283      * Determines whether this JDOField is serializable or not.  
284      * @return <code>true</code> if the field is serializable,
285      * <code>false</code> otherwise
286      */
287     public boolean isSerializable() {
288         return serializable;
289     }
290 
291     /*** 
292      * Set whether this JDOField is serializable or not.
293      * @param serializable if <code>true</code>, the JDOField is serializable;
294      * otherwise, it is not
295      * @exception ModelException if impossible
296      */
297     public void setSerializable(boolean serializable) throws ModelException {
298         this.serializable = serializable;
299     }
300 
301     /*** 
302      * Get the name of the field specified in a mappedBy attribute in the
303      * metadata. The method returns <code>null</code> if the metadata for this
304      * field does not specify the mappedBy attribute.  Note that this 
305      * can be provided at the field level to help population of the model, 
306      * but should only be specified on a field that has a corresponding
307      * relationship.
308      * @return the mappedBy field name if available; <code>null</code>
309      * otherwise.
310      */
311     public String getMappedByName() {
312         return mappedByName;
313     }
314 
315     /***
316      * Set the name of the field specified in a mappedBy attribute in the
317      * metadata.  Note that this can be provided at the field level to 
318      * help population of the model, but should only be specified on a 
319      * field that has a corresponding relationship.
320      * @param mappedByName the mappedBy field name.
321      * @exception ModelException if impossible
322      */
323     public void setMappedByName(String mappedByName) throws ModelException {
324         String oldMappedByName = this.mappedByName;
325         this.mappedByName = mappedByName;
326         UnresolvedRelationshipHelper info = getUnresolvedRelationshipHelper();
327         if (oldMappedByName != null) {
328             // remove old mappedByName from unresolved relationship helper
329             info.remove(oldMappedByName, this);
330         }
331         if (mappedByName != null) {
332             // update unresolved relationship helper
333             info.register(mappedByName, this);
334         }
335     }
336 
337     /***
338      * Get the relationship information for this JDOField. The method 
339      * returns null if the field is not part of a relationship 
340      * (e.g. it is a primitive type field).
341      * @return relationship info of this JDOField or <code>null</code> if 
342      * this JDOField is not a relationship
343      */
344     public JDORelationship getRelationship() {
345         if (relationship != null) {
346             // return relationship, if explicitly set by the setter
347             return relationship;
348         }
349         
350         // not set => calculate
351 
352         if (getPersistenceModifier() == PersistenceModifier.NONE)
353             // field has persistence modifier none => cannot be a relationship
354             return null;
355                             
356         // check the type if available
357         JDORelationship rel = null;
358         JavaType type = getType();
359         if (type != null) {
360             if (type.isValue() || TypeSupport.isValueArrayType(type)) {
361                 // no relationship
362                 rel = null;
363             }
364             else if (type.isJDOSupportedCollection()) {
365                 rel = createJDOCollectionInternal();
366             }
367             else if (type.isJDOSupportedMap()) {
368                 rel = createJDOMapInternal();
369             }
370             else if (type.isArray()) {
371                 rel = createJDOArrayInternal();
372             }
373             else {
374                 rel = createJDOReferenceInternal();
375             }
376         }
377         return rel;
378     }
379 
380     /***
381      * Set the relationship information for this JDOField.
382      * @param relationship the JDORelationship instance
383      */
384     public void setRelationship(JDORelationship relationship) 
385         throws ModelException {
386         JDORelationship old = this.relationship;
387         if (old != null) {
388             old.setInverseRelationship(null);
389         }
390         this.relationship = relationship;
391     }
392 
393     /***
394      * Creates and returns a new JDOReference instance. 
395      * This method automatically binds the new JDOReference to this JDOField. 
396      * The following holds true:
397      * <ul>
398      * <li> Method {@link #getRelationship} returns the new created instance
399      * <li> <code>this.getRelationship().getDeclaringField() == this</code>
400      * </ul> 
401      * @return a new JDOReference instance bound to this JDOField
402      * @exception ModelException if impossible
403      */
404     public JDOReference createJDOReference() throws ModelException {
405         JDOReference ref = createJDOReferenceInternal();
406         setRelationship(ref);
407         return ref;
408     }
409 
410     /***
411      * Creates and returns a new JDOCollection instance. 
412      * This method automatically binds the new JDOCollection to this JDOField. 
413      * The following holds true:
414      * <ul>
415      * <li> Method {@link #getRelationship} returns the new created instance
416      * <li> <code>this.getRelationship().getDeclaringField() == this</code>
417      * </ul> 
418      * @return a new JDOCollection instance bound to this JDOField
419      * @exception ModelException if impossible
420      */
421     public JDOCollection createJDOCollection() throws ModelException {
422         JDOCollection col = createJDOCollectionInternal();
423         setRelationship(col);
424         return col;
425     }
426 
427     /***
428      * Creates and returns a new JDOArray instance. 
429      * This method automatically binds the new JDOArray to this JDOField. 
430      * The following holds true:
431      * <ul>
432      * <li> Method {@link #getRelationship} returns the new created instance
433      * <li> <code>this.getRelationship().getDeclaringField() == this</code>
434      * </ul> 
435      * @return a new JDOArray instance bound to this JDOField
436      * @exception ModelException if impossible
437      */
438     public JDOArray createJDOArray() throws ModelException {
439         JDOArray array = createJDOArrayInternal();
440         setRelationship(array);
441         return array;
442     }
443 
444     /***
445      * Creates and returns a new JDOMap instance. 
446      * This method automatically binds the new JDOMap to this JDOField. 
447      * The following holds true:
448      * <ul>
449      * <li> Method {@link #getRelationship} returns the new created instance
450      * <li> <code>this.getRelationship().getDeclaringField() == this</code>
451      * </ul> 
452      * @return a new JDOMap instance bound to this JDOField
453      * @exception ModelException if impossible
454      */
455     public JDOMap createJDOMap() throws ModelException {
456         JDOMap map = createJDOMapInternal();
457         setRelationship(map);
458         return map;
459     }
460 
461     /***
462      * Convenience method to check the persistence modifier from this JDOField.
463      * @return <code>true</code> if this field has the  
464      * {@link PersistenceModifier#PERSISTENT} modifier; <code>false</code> 
465      * otherwise
466      */
467     public boolean isPersistent() {
468         switch (getPersistenceModifier()) {
469         case PersistenceModifier.PERSISTENT:
470             return true;
471         case PersistenceModifier.POSSIBLY_PERSISTENT:
472             // Enable assertion as soon as the enhancer sets the java modifier.
473             //Assertion.affirm(javaModifier, 
474             //                 msg.msg("ERR_MissingJavaModifier", 
475             //                 getDeclaringClass().getName() + "." + getName()));
476             int mod = getJavaField().getModifiers();
477             return !(Modifier.isStatic(mod) || Modifier.isFinal(mod) || 
478                      Modifier.isTransient(mod));
479         }
480         return false;
481     }
482 
483     /***
484      * Convenience method to check the persistence modifier from this JDOField.
485      * @return <code>true</code> if this field has the  
486      * {@link PersistenceModifier#TRANSACTIONAL} modifier; <code>false</code> 
487      * otherwise
488      */
489     public boolean isTransactional() {
490         return (getPersistenceModifier() == PersistenceModifier.TRANSACTIONAL);
491     }
492     
493     /***
494      * Convenience method to check the persistence modifier from this JDOField.
495      * A field is a managed field, if it has the persistence-modifier 
496      * {@link PersistenceModifier#PERSISTENT} or 
497      * {@link PersistenceModifier#TRANSACTIONAL}.
498      * @return <code>true</code> if this field is a managed field; 
499      * <code>false</code> otherwise     
500      */
501     public boolean isManaged() {
502         // For now treat POSSIBLY_PERSISTENT as PERSISTENT. This will be removed 
503         // as soon as the enhancer fully supports POSSIBLY_PERSISTENT
504         int persistenceModifier = getPersistenceModifier();
505         return (persistenceModifier == PersistenceModifier.PERSISTENT) ||
506                (persistenceModifier == PersistenceModifier.POSSIBLY_PERSISTENT) || 
507                (persistenceModifier == PersistenceModifier.TRANSACTIONAL);
508     }
509 
510     /***
511      * Convenience method to check whether this field is a relationship field.
512      * @return <code>true</code> if this field is a relationship;
513      * <code>false</code> otherwise
514      */
515     public boolean isRelationship() {
516         return getRelationship() != null;
517     }
518 
519     /***
520      * Convenience method to check whether this field represents a property.
521      * @return <code>true</code> if this field represents a property; 
522      * <code>false</code> otherwise
523      */
524     public boolean isProperty() {
525         return false;
526     }
527 
528     /***
529      * Get the JavaType representation of the type of the field.
530      * @return JavaType representation of the type of this field.
531      */
532     public JavaType getType() {
533         JavaField field = getJavaField();
534         return (field == null) ? null : field.getType();
535     }
536     
537     /***
538      * Returns the absolute field number of this JDOField.
539      * @return the absolute field number
540      */
541     public int getFieldNumber() {
542         int fieldNumber = getRelativeFieldNumber();
543         if (fieldNumber > -1) {
544             // >-1 denotes a managed field
545             fieldNumber += getDeclaringClass().getInheritedManagedFieldCount();
546         }
547         return fieldNumber;
548     }
549 
550     /***
551      * Returns the relative field number of this JDOField.
552      * @return the relative field number
553      */
554     public int getRelativeFieldNumber() {
555         JDOField[] fields = getDeclaringClass().getDeclaredManagedFields();
556         List fieldList = Arrays.asList(fields);
557         return fieldList.indexOf(this);
558     }
559 
560     //========= Internal helper methods ==========
561 
562     /***
563      * Creates and returns a new JDOReference instance. 
564      * This method automatically sets this JDOField as the declaring field of 
565      * the returned instance.
566      * @return a new JDOReference instance bound to this JDOField
567      */
568     protected JDOReference createJDOReferenceInternal() {
569         JDOReferenceImplDynamic ref = new JDOReferenceImplDynamic();
570         // update relationship JDORelationship->JDOField
571         ref.setDeclaringField(this);
572         return ref;
573     }
574 
575     /***
576      * Creates and returns a new JDOCollection instance. 
577      * This method automatically this JDOField as the declaring field of 
578      * the returned instance.
579      * @return a new JDOCollection instance bound to this JDOField
580      */
581     protected JDOCollection createJDOCollectionInternal() {
582         JDOCollectionImplDynamic collection = new JDOCollectionImplDynamic();
583         // update relationship JDORelationship->JDOField
584         collection.setDeclaringField(this);
585         return collection;
586     }
587 
588     /***
589      * Creates and returns a new JDOArray instance. 
590      * This method automatically this JDOField as the declaring field of 
591      * the returned instance.
592      * @return a new JDOArray instance bound to this JDOField
593      */
594     protected JDOArray createJDOArrayInternal() {
595         JDOArrayImplDynamic array = new JDOArrayImplDynamic();
596         // update relationship JDORelationship->JDOField
597         array.setDeclaringField(this);
598         return array;
599     }
600 
601     /***
602      * Creates and returns a new JDOMap instance. 
603      * This method automatically this JDOField as the declaring field of 
604      * the returned instance.
605      * @return a new JDOMap instance bound to this JDOField
606      */
607     protected JDOMap createJDOMapInternal() {
608         JDOMapImplDynamic map = new JDOMapImplDynamic();
609         // update relationship JDORelationship->JDOField
610         map.setDeclaringField(this);
611         return map;
612     }
613 
614     /***
615      * Returns <code>true</code> if the name of this JDOField has the
616      * prefix jdo. 
617      * @return <code>true</code> if the name of this JDOField has the
618      * prefix jdo; <code>false</code> otherwise.
619      */
620     private boolean nameHasJDOPrefix() {
621         String name = getName();
622         return (name != null) && name.startsWith("jdo"); //NOI18N
623     }
624 
625     /*** 
626      * Returns the UnresolvedRelationshipHelper instance from the declaring
627      * JDOModel instacne of the declaring JDOClass.
628      * @return the current UnresolvedRelationshipHelper
629      */
630     UnresolvedRelationshipHelper getUnresolvedRelationshipHelper() {
631         return ((JDOModelImplDynamic) getDeclaringClass().getDeclaringModel()).
632             getUnresolvedRelationshipHelper();
633     }
634     
635 }