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