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