001: /**********************************************************************
002: Copyright (c) 2007 Andy Jefferson and others. All rights reserved.
003: Licensed under the Apache License, Version 2.0 (the "License");
004: you may not use this file except in compliance with the License.
005: You may obtain a copy of the License at
006:
007: http://www.apache.org/licenses/LICENSE-2.0
008:
009: Unless required by applicable law or agreed to in writing, software
010: distributed under the License is distributed on an "AS IS" BASIS,
011: WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
012: See the License for the specific language governing permissions and
013: limitations under the License.
014:
015: Contributors:
016: ...
017: **********************************************************************/package org.jpox.state;
018:
019: import java.util.ArrayList;
020: import java.util.Collection;
021: import java.util.HashMap;
022: import java.util.Iterator;
023: import java.util.Map;
024: import java.util.Set;
025:
026: import org.jpox.ClassLoaderResolver;
027: import org.jpox.ObjectManager;
028: import org.jpox.StateManager;
029: import org.jpox.exceptions.JPOXUserException;
030: import org.jpox.metadata.AbstractClassMetaData;
031: import org.jpox.metadata.AbstractMemberMetaData;
032: import org.jpox.metadata.Relation;
033: import org.jpox.sco.SCOCollection;
034: import org.jpox.util.JPOXLogger;
035: import org.jpox.util.Localiser;
036: import org.jpox.util.StringUtils;
037:
038: /**
039: * Manager for relationships of a class.
040: * Performs management of (bidirectional) relations.
041: * If one side is set yet the other isnt, corrects the other side.
042: *
043: * @version $Revision$
044: */
045: public class RelationshipManager {
046: /** Localiser for messages. */
047: protected static final Localiser LOCALISER = Localiser
048: .getInstance("org.jpox.Localisation");
049:
050: /** StateManager for the object we are managing the relationships for. */
051: final StateManager ownerSM;
052:
053: /** Object being managed. */
054: final Object pc;
055:
056: /**
057: * Map of bidirectional field "changes".
058: * For 1-1, N-1 fields the "change" is actually the original value (for later comparison).
059: * For 1-N, M-N fields the "change" is an ArrayList of RelationChange objects.
060: */
061: final Map fieldChanges;
062:
063: /**
064: * Constructor.
065: * @param sm StateManager for the object that we are managing relations for.
066: */
067: public RelationshipManager(StateManager sm) {
068: this .ownerSM = sm;
069: this .pc = sm.getObject();
070: this .fieldChanges = new HashMap();
071:
072: // Mark the StateManager as having its relations managed
073: ownerSM.getObjectManager().markManagedRelationDirty(sm);
074: }
075:
076: private static final int ADD_OBJECT = 1; // Element added to a collection
077: private static final int REMOVE_OBJECT = 2; // Element removed from a collection
078:
079: /**
080: * Definition of a change in a relation.
081: * @version $Revision$
082: */
083: private class RelationChange {
084: int type;
085: Object value;
086:
087: public RelationChange(int type, Object val) {
088: this .type = type;
089: this .value = val;
090: }
091: }
092:
093: /**
094: * Convenience method to clear all fields from being managed.
095: */
096: public void clearFields() {
097: fieldChanges.clear();
098: }
099:
100: /**
101: * Method that is called when the user calls setXXX() on a field.
102: * @param fieldNumber Number of the field
103: * @param oldValue The old value
104: * @param newValue The new value
105: */
106: public void relationChange(int fieldNumber, Object oldValue,
107: Object newValue) {
108: if (ownerSM.getObjectManager().isManagingRelations()) {
109: return;
110: }
111:
112: Integer fieldKey = new Integer(fieldNumber);
113: if (!fieldChanges.containsKey(fieldKey)) {
114: // Store the original value only - only good for PC fields really
115: AbstractClassMetaData cmd = ownerSM.getClassMetaData();
116: AbstractMemberMetaData mmd = cmd
117: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
118: int relationType = mmd.getRelationType(ownerSM
119: .getObjectManager().getClassLoaderResolver());
120: if (relationType == Relation.ONE_TO_ONE_BI
121: || relationType == Relation.MANY_TO_ONE_BI) {
122: // Only allow for set of PC field (ignore set of Collection fields)
123: fieldChanges.put(fieldKey, oldValue);
124: }
125: }
126: }
127:
128: /**
129: * Method to register a change in the contents of a container field, with an object being added.
130: * @param fieldNumber Number of the field
131: * @param val Value being added
132: */
133: public void relationAdd(int fieldNumber, Object val) {
134: if (ownerSM.getObjectManager().isManagingRelations()) {
135: return;
136: }
137:
138: AbstractClassMetaData cmd = ownerSM.getClassMetaData();
139: AbstractMemberMetaData mmd = cmd
140: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
141: int relationType = mmd.getRelationType(ownerSM
142: .getObjectManager().getClassLoaderResolver());
143: if (relationType != Relation.ONE_TO_MANY_BI
144: && relationType != Relation.MANY_TO_MANY_BI) {
145: return;
146: }
147:
148: Integer fieldKey = new Integer(fieldNumber);
149: Object changes = fieldChanges.get(fieldKey);
150: ArrayList changeList = null;
151: if (changes == null) {
152: changeList = new ArrayList();
153: } else {
154: // Assume we have an ArrayList
155: changeList = (ArrayList) changes;
156: }
157: RelationChange change = new RelationChange(ADD_OBJECT, val);
158: changeList.add(change);
159: fieldChanges.put(fieldKey, changeList);
160: }
161:
162: /**
163: * Method to register a change in the contents of a container field, with an object being removed.
164: * @param fieldNumber Number of the field
165: * @param val Value being removed
166: */
167: public void relationRemove(int fieldNumber, Object val) {
168: if (ownerSM.getObjectManager().isManagingRelations()) {
169: return;
170: }
171:
172: AbstractClassMetaData cmd = ownerSM.getClassMetaData();
173: AbstractMemberMetaData mmd = cmd
174: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
175: int relationType = mmd.getRelationType(ownerSM
176: .getObjectManager().getClassLoaderResolver());
177: if (relationType != Relation.ONE_TO_MANY_BI
178: && relationType != Relation.MANY_TO_MANY_BI) {
179: return;
180: }
181:
182: Integer fieldKey = new Integer(fieldNumber);
183: Object changes = fieldChanges.get(fieldKey);
184: ArrayList changeList = null;
185: if (changes == null) {
186: changeList = new ArrayList();
187: } else {
188: changeList = (ArrayList) changes;
189: }
190: RelationChange change = new RelationChange(REMOVE_OBJECT, val);
191: changeList.add(change);
192: }
193:
194: /**
195: * Accessor for whether a field is being managed.
196: * @param fieldNumber Number of the field
197: * @return Whether it is currently managed
198: */
199: private boolean managesField(int fieldNumber) {
200: return fieldChanges.containsKey(new Integer(fieldNumber));
201: }
202:
203: /**
204: * Method to check for consistency the managed relations of this object with the related objects.
205: */
206: public void checkConsistency() {
207: Set entries = fieldChanges.entrySet();
208: Iterator iter = entries.iterator();
209: AbstractClassMetaData cmd = ownerSM.getClassMetaData();
210: ObjectManager om = ownerSM.getObjectManager();
211: while (iter.hasNext()) {
212: Map.Entry entry = (Map.Entry) iter.next();
213: int fieldNumber = ((Integer) entry.getKey()).intValue();
214: AbstractMemberMetaData mmd = cmd
215: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
216: ClassLoaderResolver clr = om.getClassLoaderResolver();
217: Object oldValue = entry.getValue();
218: int relationType = mmd.getRelationType(clr);
219: if (relationType == Relation.ONE_TO_ONE_BI) {
220: // 1-1 bidirectional
221: Object newValue = ownerSM.provideField(fieldNumber);
222: checkOneToOneBidirectionalRelation(mmd, clr, om,
223: oldValue, newValue);
224: } else if (relationType == Relation.MANY_TO_ONE_BI) {
225: // N-1 bidirectional
226: Object newValue = ownerSM.provideField(fieldNumber);
227: checkManyToOneBidirectionalRelation(mmd, clr, om,
228: oldValue, newValue);
229: } else if (relationType == Relation.ONE_TO_MANY_BI) {
230: // 1-N bidirectional
231: ArrayList changes = (ArrayList) oldValue;
232: checkOneToManyBidirectionalRelation(mmd, clr, om,
233: changes);
234: } else if (relationType == Relation.MANY_TO_MANY_BI) {
235: // M-N bidirectional
236: ArrayList changes = (ArrayList) oldValue;
237: checkManyToManyBidirectionalRelation(mmd, clr, om,
238: changes);
239: }
240: }
241: }
242:
243: /**
244: * Method to process the (bidirectional) relations for this object.
245: */
246: public void process() {
247: Set entries = fieldChanges.entrySet();
248: Iterator iter = entries.iterator();
249: AbstractClassMetaData cmd = ownerSM.getClassMetaData();
250: ObjectManager om = ownerSM.getObjectManager();
251: while (iter.hasNext()) {
252: Map.Entry entry = (Map.Entry) iter.next();
253: int fieldNumber = ((Integer) entry.getKey()).intValue();
254: AbstractMemberMetaData mmd = cmd
255: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
256: ClassLoaderResolver clr = om.getClassLoaderResolver();
257: Object oldValue = entry.getValue();
258: int relationType = mmd.getRelationType(clr);
259: if (relationType == Relation.ONE_TO_ONE_BI) {
260: // 1-1 bidirectional
261: Object newValue = ownerSM.provideField(fieldNumber);
262: processOneToOneBidirectionalRelation(mmd, clr, om,
263: oldValue, newValue);
264: } else if (relationType == Relation.MANY_TO_ONE_BI) {
265: // N-1 bidirectional
266: Object newValue = ownerSM.provideField(fieldNumber);
267: processManyToOneBidirectionalRelation(mmd, clr, om,
268: oldValue, newValue);
269: } else if (relationType == Relation.ONE_TO_MANY_BI) {
270: // 1-N bidirectional
271: ArrayList changes = (ArrayList) oldValue;
272: processOneToManyBidirectionalRelation(mmd, clr, om,
273: changes);
274: } else if (relationType == Relation.MANY_TO_MANY_BI) {
275: // M-N bidirectional
276: ArrayList changes = (ArrayList) oldValue;
277: processManyToManyBidirectionalRelation(mmd, clr, om,
278: changes);
279: }
280: }
281: }
282:
283: /**
284: * Method to check the consistency of the passed field as 1-1.
285: * Processes the case where we had a 1-1 field set at this side previously to some value and now to
286: * some other value. We need to make sure that all of the affected objects are now related consistently.
287: * Taking an example <pre>a.b = b1; a.b = b2;</pre> so A's b field is changed from b1 to b2.
288: * The following changes are likely to be necessary
289: * <ul>
290: * <li>b1.a = null - so we null out the old related objects link back to this object</li>
291: * <li>b2.oldA = null - if b2 was previously related to a different A, null out that objects link to b2</li>
292: * <li>b2.a = a - set the link from b2 back to a so it is bidirectional</li>
293: * </ul>
294: * @param mmd MetaData for the field
295: * @param clr ClassLoader resolver
296: * @param om ObjectManager
297: * @param oldValue The old value
298: * @param newValue The new value
299: */
300: protected void checkOneToOneBidirectionalRelation(
301: AbstractMemberMetaData mmd, ClassLoaderResolver clr,
302: ObjectManager om, Object oldValue, Object newValue) {
303: if (newValue != null) {
304: // Previously had "a.b = b1"; Now have "a.b = b2"
305: // Check that the new value hasnt been assigned to something other than this object
306: AbstractMemberMetaData relatedMmd = mmd
307: .getRelatedMemberMetaDataForObject(clr, pc,
308: newValue);
309: org.jpox.StateManager newSM = om.findStateManager(newValue);
310: if (newSM != null && relatedMmd != null) {
311: newSM.loadField(relatedMmd.getAbsoluteFieldNumber());
312: Object newValueFieldValue = newSM
313: .provideField(relatedMmd
314: .getAbsoluteFieldNumber());
315: if (newValueFieldValue != pc) {
316: RelationshipManager newRelMgr = newSM
317: .getRelationshipManager();
318: if (newRelMgr != null
319: && newRelMgr.managesField(relatedMmd
320: .getAbsoluteFieldNumber())) {
321: // New value has had its side of the relation changed to a different value altogether!
322: if (newValueFieldValue == null) {
323: String msg = LOCALISER.msg("013003",
324: StringUtils.toJVMIDString(pc), mmd
325: .getName(), StringUtils
326: .toJVMIDString(newValue),
327: relatedMmd.getName());
328: JPOXLogger.PERSISTENCE.error(msg);
329: throw new JPOXUserException(msg);
330: } else {
331: String msg = LOCALISER
332: .msg(
333: "013002",
334: StringUtils
335: .toJVMIDString(pc),
336: mmd.getName(),
337: StringUtils
338: .toJVMIDString(newValue),
339: relatedMmd.getName(),
340: StringUtils
341: .toJVMIDString(newValueFieldValue));
342: JPOXLogger.PERSISTENCE.error(msg);
343: throw new JPOXUserException(msg);
344: }
345: }
346: }
347: }
348: }
349: }
350:
351: /**
352: * Method to check the consistency of the passed field as 1-N.
353: * @param mmd MetaData for the field
354: * @param clr ClassLoader resolver
355: * @param om ObjectManager
356: * @param changes List of changes to the collection
357: */
358: protected void checkOneToManyBidirectionalRelation(
359: AbstractMemberMetaData mmd, ClassLoaderResolver clr,
360: ObjectManager om, ArrayList changes) {
361: Iterator iter = changes.iterator();
362: while (iter.hasNext()) {
363: RelationChange change = (RelationChange) iter.next();
364: if (change.type == ADD_OBJECT) {
365: if (ownerSM.getObjectManager().getApiAdapter()
366: .isDeleted(change.value)) {
367: // The element was added but was then the element object was deleted!
368: throw new JPOXUserException(LOCALISER.msg("013008",
369: StringUtils.toJVMIDString(pc), mmd
370: .getName(), StringUtils
371: .toJVMIDString(change.value)));
372: } else {
373: AbstractMemberMetaData relatedMmd = mmd
374: .getRelatedMemberMetaData(clr)[0];
375: org.jpox.StateManager newElementSM = om
376: .findStateManager(change.value);
377: if (newElementSM != null) {
378: newElementSM.loadField(relatedMmd
379: .getAbsoluteFieldNumber());
380: RelationshipManager newElementRelMgr = newElementSM
381: .getRelationshipManager();
382: if (newElementRelMgr != null
383: && newElementRelMgr
384: .managesField(relatedMmd
385: .getAbsoluteFieldNumber())) {
386: // Element has had the owner set, so make sure it is set to this object
387: Object newValueFieldValue = newElementSM
388: .provideField(relatedMmd
389: .getAbsoluteFieldNumber());
390: if (newValueFieldValue != pc
391: && newValueFieldValue != null) {
392: // The element has a different owner than the PC with this collection
393: // This catches cases where the user has set the wrong owner, and also
394: // will catch cases where the user has added it to two collections
395: throw new JPOXUserException(
396: LOCALISER
397: .msg(
398: "013009",
399: StringUtils
400: .toJVMIDString(pc),
401: mmd.getName(),
402: StringUtils
403: .toJVMIDString(change.value),
404: StringUtils
405: .toJVMIDString(newValueFieldValue)));
406: }
407: }
408: }
409: }
410: } else if (change.type == REMOVE_OBJECT) {
411: if (ownerSM.getObjectManager().getApiAdapter()
412: .isDeleted(change.value)) {
413: // The element was removed and was then the element object was deleted so do nothing
414: } else {
415: AbstractMemberMetaData relatedMmd = mmd
416: .getRelatedMemberMetaData(clr)[0];
417: org.jpox.StateManager newElementSM = om
418: .findStateManager(change.value);
419: if (newElementSM != null) {
420: newElementSM.loadField(relatedMmd
421: .getAbsoluteFieldNumber());
422: RelationshipManager newElementRelMgr = newElementSM
423: .getRelationshipManager();
424: if (newElementRelMgr != null
425: && newElementRelMgr
426: .managesField(relatedMmd
427: .getAbsoluteFieldNumber())) {
428: // Element has had the owner set, so make sure it is not set to this object
429: Object newValueFieldValue = newElementSM
430: .provideField(relatedMmd
431: .getAbsoluteFieldNumber());
432: if (newValueFieldValue == pc) {
433: // The element was removed from the collection, but was updated to have its owner
434: // set to the collection owner!
435: throw new JPOXUserException(
436: LOCALISER
437: .msg(
438: "013010",
439: StringUtils
440: .toJVMIDString(pc),
441: mmd.getName(),
442: StringUtils
443: .toJVMIDString(change.value)));
444: }
445: }
446: }
447: }
448: }
449: }
450: }
451:
452: /**
453: * Method to check the consistency of the passed field as N-1.
454: * Processes the case where we had an N-1 field set at this side previously to some value and now to
455: * some other value.That is, this object was in some collection/map originally, and now should be in some
456: * other collection/map. So in terms of an example this object "a" was in collection "b1.as" before and is
457: * now in "b2.as". The following changes are likely to be necessary
458: * <ul>
459: * <li>b1.getAs().remove(a) - remove it from b1.as if still present</li>
460: * <li>b2.getAs().add(a) - add it to b1.as if not present</li>
461: * </ul>
462: * @param mmd MetaData for the field
463: * @param clr ClassLoader resolver
464: * @param om ObjectManager
465: * @param oldValue The old value
466: * @param newValue The new value
467: */
468: protected void checkManyToOneBidirectionalRelation(
469: AbstractMemberMetaData mmd, ClassLoaderResolver clr,
470: ObjectManager om, Object oldValue, Object newValue) {
471: // TODO Implement N-1
472: }
473:
474: /**
475: * Method to check consistency of the passed field as M-N.
476: * @param mmd MetaData for the field
477: * @param clr ClassLoader resolver
478: * @param om ObjectManager
479: * @param changes List of changes to the collection
480: */
481: protected void checkManyToManyBidirectionalRelation(
482: AbstractMemberMetaData mmd, ClassLoaderResolver clr,
483: ObjectManager om, ArrayList changes) {
484: // TODO Implement M-N
485: }
486:
487: /**
488: * Method to process all 1-1 bidir fields.
489: * Processes the case where we had a 1-1 field set at this side previously to some value and now to
490: * some other value. We need to make sure that all of the affected objects are now related consistently.
491: * Taking an example <pre>a.b = b1; a.b = b2;</pre> so A's b field is changed from b1 to b2.
492: * The following changes are likely to be necessary
493: * <ul>
494: * <li>b1.a = null - so we null out the old related objects link back to this object</li>
495: * <li>b2.oldA = null - if b2 was previously related to a different A, null out that objects link to b2</li>
496: * <li>b2.a = a - set the link from b2 back to a so it is bidirectional</li>
497: * </ul>
498: * @param mmd MetaData for the field
499: * @param clr ClassLoader resolver
500: * @param om ObjectManager
501: * @param oldValue The old value
502: * @param newValue The new value
503: */
504: protected void processOneToOneBidirectionalRelation(
505: AbstractMemberMetaData mmd, ClassLoaderResolver clr,
506: ObjectManager om, Object oldValue, Object newValue) {
507: if (oldValue != null) {
508: // Previously had "a.b = b1"; "a.b" has been changed
509: // Need to remove from the other side if still set
510: AbstractMemberMetaData relatedMmd = mmd
511: .getRelatedMemberMetaDataForObject(clr, pc,
512: oldValue);
513: org.jpox.StateManager oldSM = om.findStateManager(oldValue);
514: if (oldSM != null) {
515: oldSM.loadField(relatedMmd.getAbsoluteFieldNumber());
516: Object oldValueFieldValue = oldSM
517: .provideField(relatedMmd
518: .getAbsoluteFieldNumber());
519: if (oldValueFieldValue == null) {
520: // Set to null so nothing to do
521: } else if (oldValueFieldValue == pc) {
522: // Still set to this object, so null out the other objects relation
523: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
524: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
525: "013004", StringUtils
526: .toJVMIDString(oldValue),
527: relatedMmd.getFullFieldName(),
528: StringUtils.toJVMIDString(pc),
529: StringUtils.toJVMIDString(newValue)));
530: }
531: oldSM.replaceFieldValue(relatedMmd
532: .getAbsoluteFieldNumber(), null);
533: }
534: }
535: }
536: if (newValue != null) {
537: // Previously had "a.b = b1"; Now have "a.b = b2"
538: // Need to set the other side if not yet set, and unset any related old value on the other side
539: AbstractMemberMetaData relatedMmd = mmd
540: .getRelatedMemberMetaDataForObject(clr, pc,
541: newValue);
542: org.jpox.StateManager newSM = om.findStateManager(newValue);
543: if (newSM != null && relatedMmd != null) {
544: newSM.loadField(relatedMmd.getAbsoluteFieldNumber());
545: Object newValueFieldValue = newSM
546: .provideField(relatedMmd
547: .getAbsoluteFieldNumber());
548: if (newValueFieldValue == null) {
549: // Was set to null so set to our object
550: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
551: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
552: "013005", StringUtils
553: .toJVMIDString(newValue),
554: relatedMmd.getFullFieldName(),
555: StringUtils.toJVMIDString(pc)));
556: }
557: newSM.replaceFieldValue(relatedMmd
558: .getAbsoluteFieldNumber(), pc);
559: } else if (newValueFieldValue != pc) {
560: // Was set to different object, so null out the other objects relation
561: org.jpox.StateManager newValueFieldSM = om
562: .findStateManager(newValueFieldValue);
563: if (newValueFieldSM != null) {
564: // Null out the field of the related object of the new value
565: newValueFieldSM.loadField(mmd
566: .getAbsoluteFieldNumber());
567: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
568: JPOXLogger.PERSISTENCE
569: .debug(LOCALISER
570: .msg(
571: "013004",
572: StringUtils
573: .toJVMIDString(newValueFieldValue),
574: mmd
575: .getFullFieldName(),
576: StringUtils
577: .toJVMIDString(newValue),
578: StringUtils
579: .toJVMIDString(pc)));
580: }
581: newValueFieldSM.replaceFieldValue(mmd
582: .getAbsoluteFieldNumber(), null);
583: }
584: // Update the field of the new value to our object
585: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
586: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg(
587: "013005", StringUtils
588: .toJVMIDString(newValue),
589: relatedMmd.getFullFieldName(),
590: StringUtils.toJVMIDString(pc)));
591: }
592: newSM.replaceFieldValue(relatedMmd
593: .getAbsoluteFieldNumber(), pc);
594: }
595: }
596: }
597: }
598:
599: /**
600: * Method to process all 1-N bidir fields.
601: * @param mmd MetaData for the field
602: * @param clr ClassLoader resolver
603: * @param om ObjectManager
604: * @param changes List of changes to the collection
605: */
606: protected void processOneToManyBidirectionalRelation(
607: AbstractMemberMetaData mmd, ClassLoaderResolver clr,
608: ObjectManager om, ArrayList changes) {
609: Iterator iter = changes.iterator();
610: while (iter.hasNext()) {
611: RelationChange change = (RelationChange) iter.next();
612: if (change.type == ADD_OBJECT) {
613: } else if (change.type == REMOVE_OBJECT) {
614: }
615: }
616: // TODO Implement 1-N
617: }
618:
619: /**
620: * Method to process all N-1 bidir fields.
621: * Processes the case where we had an N-1 field set at this side previously to some value and now to
622: * some other value.That is, this object was in some collection/map originally, and now should be in some
623: * other collection/map. So in terms of an example this object "a" was in collection "b1.as" before and is
624: * now in "b2.as". The following changes are likely to be necessary
625: * <ul>
626: * <li>b1.getAs().remove(a) - remove it from b1.as if still present</li>
627: * <li>b2.getAs().add(a) - add it to b1.as if not present</li>
628: * </ul>
629: * @param mmd MetaData for the field
630: * @param clr ClassLoader resolver
631: * @param om ObjectManager
632: * @param oldValue The old value
633: * @param newValue The new value
634: */
635: protected void processManyToOneBidirectionalRelation(
636: AbstractMemberMetaData mmd, ClassLoaderResolver clr,
637: ObjectManager om, Object oldValue, Object newValue) {
638: if (oldValue != null) {
639: // Has been removed from a Collection/Map
640: AbstractMemberMetaData relatedMmd = mmd
641: .getRelatedMemberMetaDataForObject(clr, pc,
642: oldValue);
643: org.jpox.StateManager oldSM = om.findStateManager(oldValue);
644: if (oldSM != null && relatedMmd != null) {
645: oldSM.loadField(relatedMmd.getAbsoluteFieldNumber());
646: Object oldContainerValue = oldSM
647: .provideField(relatedMmd
648: .getAbsoluteFieldNumber());
649: if (oldContainerValue instanceof Collection) {
650: Collection oldColl = (Collection) oldContainerValue;
651: if (oldColl.contains(pc)) {
652: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
653: JPOXLogger.PERSISTENCE
654: .debug(LOCALISER
655: .msg(
656: "013006",
657: StringUtils
658: .toJVMIDString(pc),
659: mmd
660: .getFullFieldName(),
661: relatedMmd
662: .getFullFieldName(),
663: StringUtils
664: .toJVMIDString(oldValue)));
665: }
666:
667: if (oldColl instanceof SCOCollection) {
668: // Avoid any cascade deletes that could have been fired by this action
669: ((SCOCollection) oldColl).remove(pc, false);
670: } else {
671: oldColl.remove(pc);
672: }
673: }
674: }
675: }
676: }
677: if (newValue != null) {
678: // Has been added to a Collection/Map
679: AbstractMemberMetaData relatedMmd = mmd
680: .getRelatedMemberMetaDataForObject(clr, pc,
681: newValue);
682: org.jpox.StateManager newSM = om.findStateManager(newValue);
683: if (newSM != null && relatedMmd != null) {
684: newSM.loadField(relatedMmd.getAbsoluteFieldNumber());
685: Object newContainerValue = newSM
686: .provideField(relatedMmd
687: .getAbsoluteFieldNumber());
688: if (newContainerValue instanceof Collection) {
689: Collection newColl = (Collection) newContainerValue;
690: if (!newColl.contains(pc)) {
691: if (JPOXLogger.PERSISTENCE.isDebugEnabled()) {
692: JPOXLogger.PERSISTENCE
693: .debug(LOCALISER
694: .msg(
695: "013007",
696: StringUtils
697: .toJVMIDString(pc),
698: mmd
699: .getFullFieldName(),
700: relatedMmd
701: .getFullFieldName(),
702: StringUtils
703: .toJVMIDString(newValue)));
704: }
705: newColl.add(pc);
706: }
707: }
708: }
709: }
710: }
711:
712: /**
713: * Method to process all M-N bidir fields.
714: * @param mmd MetaData for the field
715: * @param clr ClassLoader resolver
716: * @param om ObjectManager
717: * @param changes List of changes to the collection
718: */
719: protected void processManyToManyBidirectionalRelation(
720: AbstractMemberMetaData mmd, ClassLoaderResolver clr,
721: ObjectManager om, ArrayList changes) {
722: // TODO Implement M-N
723: }
724: }
|