1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
114 return persistenceModifier;
115 }
116
117
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()));
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
200 return defaultFetchGroup.booleanValue();
201 }
202
203
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
237 return embedded.booleanValue();
238 }
239
240
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
266 return javaField;
267 }
268
269
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
329 info.remove(oldMappedByName, this);
330 }
331 if (mappedByName != null) {
332
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
347 return relationship;
348 }
349
350
351
352 if (getPersistenceModifier() == PersistenceModifier.NONE)
353
354 return null;
355
356
357 JDORelationship rel = null;
358 JavaType type = getType();
359 if (type != null) {
360 if (type.isValue() || TypeSupport.isValueArrayType(type)) {
361
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
473
474
475
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
503
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
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
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
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
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
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
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");
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 }