001: /**********************************************************************
002: Copyright (c) 2004 Erik Bengtson 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: 2005 Andy Jefferson - cater for object not being persistent
017: 2007 Andy Jefferson - attach in-situ
018: ...
019: **********************************************************************/package org.jpox.store.fieldmanager;
020:
021: import java.util.Collection;
022: import java.util.Map;
023:
024: import org.jpox.StateManager;
025: import org.jpox.api.ApiAdapter;
026: import org.jpox.metadata.AbstractClassMetaData;
027: import org.jpox.metadata.AbstractMemberMetaData;
028: import org.jpox.sco.SCO;
029: import org.jpox.sco.SCOContainer;
030: import org.jpox.sco.SCOUtils;
031: import org.jpox.util.JPOXLogger;
032: import org.jpox.util.Localiser;
033: import org.jpox.util.StringUtils;
034:
035: /**
036: * Utility class to handle the attach of fields.
037: * The attachment process has 2 distinct cases to cater for.
038: * <OL>
039: * <LI>The object was detached, has been updated, and needs reattaching.</LI>
040: * <LI>The object was detached from a different datastore, and is being attached here.</LI>
041: * </OL>
042: * In the first case, the fields which are dirty have their values (and dirty flags) updated.
043: * In the second case, all fields have their fields (and dirty flags) updated.
044: * In addition this field manager allows attaching a copy, or attaching in-situ
045: *
046: * @version $Revision: 1.14 $
047: */
048: public class AttachFieldManager extends AbstractFieldManager {
049: /** Localiser for internationalisation. */
050: protected static final Localiser LOCALISER = Localiser
051: .getInstance("org.jpox.Localisation");
052:
053: /** the attached instance */
054: private final StateManager smAttached;
055:
056: /** The second class mutable fields. */
057: private final boolean[] secondClassMutableFields;
058:
059: /** The dirty fields. */
060: private final boolean dirtyFields[];
061:
062: /** Whether the attached instance is persistent yet. */
063: private final boolean persistent;
064:
065: /** Whether to cascade the attach to related fields. */
066: private final boolean cascadeAttach;
067:
068: /** Whether we should create attached copies, or attach in situ. */
069: boolean copy = true;
070:
071: /**
072: * Constructor.
073: * @param smAttached the attached instance
074: * @param secondClassMutableFields second class mutable field flags
075: * @param dirtyFields dirty field flags
076: * @param persistent whether the object is persistent
077: * @param cascadeAttach Whether to cascade any attach calls to related fields
078: */
079: public AttachFieldManager(StateManager smAttached,
080: boolean secondClassMutableFields[], boolean dirtyFields[],
081: boolean persistent, boolean cascadeAttach, boolean copy) {
082: this .smAttached = smAttached;
083: this .secondClassMutableFields = secondClassMutableFields;
084: this .dirtyFields = dirtyFields;
085: this .persistent = persistent;
086: this .cascadeAttach = cascadeAttach;
087: this .copy = copy;
088: }
089:
090: /**
091: * Method to store an object field into the attached instance.
092: * @param fieldNumber Number of the field to store
093: * @param value the value in the detached instance
094: */
095: public void storeObjectField(int fieldNumber, Object value) {
096: // Note : when doing updates always do replaceField first and makeDirty after since the replaceField
097: // can cause flush() to be called meaning that an update with null would be made before the new value
098: // makes it into the field
099: ApiAdapter api = smAttached.getObjectManager().getApiAdapter();
100: if (value == null) {
101: Object oldValue = null;
102: AbstractClassMetaData cmd = smAttached.getClassMetaData();
103: AbstractMemberMetaData fmd = cmd
104: .getMetaDataForManagedMemberAtAbsolutePosition(fieldNumber);
105: if (fmd.isDependent()) {
106: try {
107: // Get any old value of this field so we can do cascade-delete if being nulled
108: smAttached.loadFieldFromDatastore(fieldNumber);
109: } catch (Exception e) {
110: // Error loading the field so didnt exist before attaching anyway
111: }
112: oldValue = smAttached.provideField(fieldNumber);
113: }
114:
115: if (dirtyFields[fieldNumber] || !persistent) {
116: smAttached.replaceField(fieldNumber, null, true);
117: smAttached.makeDirty(fieldNumber);
118: }
119:
120: if (fmd.isDependent() && !fmd.isEmbedded()
121: && oldValue != null && value == null
122: && api.isPersistable(oldValue)) {
123: // Check for a field storing a PC where it is being nulled and the other object is dependent
124: smAttached.flush(); // Flush the nulling of the field
125: JPOXLogger.PERSISTENCE.debug(LOCALISER.msg("026026",
126: oldValue, fmd.getFullFieldName()));
127: smAttached.getObjectManager().deleteObjectInternal(
128: oldValue);
129: }
130: } else if (secondClassMutableFields[fieldNumber]) {
131: AbstractMemberMetaData fmd = smAttached.getClassMetaData()
132: .getMetaDataForManagedMemberAtAbsolutePosition(
133: fieldNumber);
134: if (fmd.isSerialized()) {
135: // SCO Field is serialised so just update the column with this new value - dont do comparisons yet
136: smAttached.replaceField(fieldNumber, value, true);
137: smAttached.makeDirty(fieldNumber);
138: } else {
139: // Make sure that the value is a SCO wrapper
140: Object oldValue = smAttached.provideField(fieldNumber);
141: SCO sco;
142: if (oldValue == null
143: || (oldValue != null && !(oldValue instanceof SCO))) {
144: // The field wasn't initialised at creation, so create an empty SCO wrapper and copy the new values in
145: if (JPOXLogger.JDO.isDebugEnabled()) {
146: JPOXLogger.JDO.debug(LOCALISER.msg("026029",
147: StringUtils.toJVMIDString(smAttached
148: .getObject()), smAttached
149: .getInternalObjectId(), fmd
150: .getName()));
151: }
152: sco = SCOUtils
153: .newSCOInstance(smAttached, fmd, fmd
154: .getType(), null, null, false,
155: false, false);
156: if (sco instanceof SCOContainer) {
157: // Load any containers to avoid update issues
158: ((SCOContainer) sco).load();
159: }
160: smAttached.replaceField(fieldNumber, sco, true);
161: } else {
162: // The field is already a SCO wrapper, so just copy the new values to it
163: sco = (SCO) oldValue;
164: }
165:
166: if (cascadeAttach) {
167: // Only trigger the cascade when required
168: if (copy) {
169: // Attach copy of the SCO
170: sco.attachCopy(value);
171: } else {
172: // Attach SCO in-situ
173: if (sco instanceof Collection) {
174: // Attach all PC elements of the collection
175: boolean elementsWithoutIdentity = SCOUtils
176: .collectionHasElementsWithoutIdentity(fmd);
177: SCOUtils.attachForCollection(smAttached,
178: ((Collection) sco).toArray(),
179: elementsWithoutIdentity);
180: sco.unsetOwner();
181: } else if (sco instanceof Map) {
182: // Attach all PC keys/values of the map
183: boolean keysWithoutIdentity = SCOUtils
184: .mapHasKeysWithoutIdentity(fmd);
185: boolean valuesWithoutIdentity = SCOUtils
186: .mapHasValuesWithoutIdentity(fmd);
187: SCOUtils.attachForMap(smAttached,
188: ((Map) sco).entrySet(),
189: keysWithoutIdentity,
190: valuesWithoutIdentity);
191: sco.unsetOwner();
192: } else {
193: // Initialise the SCO with the new value
194: sco.initialise(value, false, false);
195: }
196: }
197: }
198:
199: if (dirtyFields[fieldNumber] || !persistent) {
200: smAttached.makeDirty(fieldNumber);
201: }
202: }
203: } else if (api.isPersistable(value)) {
204: // Field containing PC object
205: if (cascadeAttach) {
206: // Determine if field is "second-class" (no identity) - if has "embedded" info or serialised
207: AbstractMemberMetaData fmd = smAttached
208: .getClassMetaData()
209: .getMetaDataForManagedMemberAtAbsolutePosition(
210: fieldNumber);
211: boolean sco = (fmd.getEmbeddedMetaData() != null
212: || fmd.isSerialized() || fmd.isEmbedded());
213:
214: if (copy) {
215: // Attach copy of the PC
216: Object pcObj = smAttached.getObjectManager()
217: .attachObjectCopy(value, sco);
218: smAttached.replaceField(fieldNumber, pcObj, true);
219: } else {
220: // Attach PC in-situ
221: smAttached.getObjectManager().attachObject(value,
222: sco);
223: }
224:
225: // Make sure the field is marked as dirty
226: if (dirtyFields[fieldNumber] || !persistent) {
227: smAttached.makeDirty(fieldNumber);
228: } else if (sco
229: && value != null
230: && smAttached.getObjectManager()
231: .getApiAdapter().isDirty(value)) {
232: smAttached.makeDirty(fieldNumber);
233: }
234: } else if (dirtyFields[fieldNumber] || !persistent) {
235: smAttached.makeDirty(fieldNumber);
236: }
237: } else {
238: if (dirtyFields[fieldNumber] || !persistent) {
239: smAttached.replaceField(fieldNumber, value, true);
240: smAttached.makeDirty(fieldNumber);
241: }
242: }
243: }
244:
245: /*
246: * (non-Javadoc)
247: * @see FieldConsumer#storeBooleanField(int, boolean)
248: */
249: public void storeBooleanField(int fieldNumber, boolean value) {
250: if (dirtyFields[fieldNumber] || !persistent) {
251: SingleValueFieldManager sfv = new SingleValueFieldManager();
252: sfv.storeBooleanField(fieldNumber, value);
253: smAttached.replaceFields(new int[] { fieldNumber }, sfv);
254: smAttached.makeDirty(fieldNumber);
255: }
256: }
257:
258: /*
259: * (non-Javadoc)
260: * @see FieldConsumer#storeByteField(int, byte)
261: */
262: public void storeByteField(int fieldNumber, byte value) {
263: if (dirtyFields[fieldNumber] || !persistent) {
264: SingleValueFieldManager sfv = new SingleValueFieldManager();
265: sfv.storeByteField(fieldNumber, value);
266: smAttached.replaceFields(new int[] { fieldNumber }, sfv);
267: smAttached.makeDirty(fieldNumber);
268: }
269: }
270:
271: /*
272: * (non-Javadoc)
273: * @see FieldConsumer#storeCharField(int, char)
274: */
275: public void storeCharField(int fieldNumber, char value) {
276: if (dirtyFields[fieldNumber] || !persistent) {
277: SingleValueFieldManager sfv = new SingleValueFieldManager();
278: sfv.storeCharField(fieldNumber, value);
279: smAttached.replaceFields(new int[] { fieldNumber }, sfv);
280: smAttached.makeDirty(fieldNumber);
281: }
282: }
283:
284: /*
285: * (non-Javadoc)
286: * @see FieldConsumer#storeDoubleField(int, double)
287: */
288: public void storeDoubleField(int fieldNumber, double value) {
289: if (dirtyFields[fieldNumber] || !persistent) {
290: SingleValueFieldManager sfv = new SingleValueFieldManager();
291: sfv.storeDoubleField(fieldNumber, value);
292: smAttached.replaceFields(new int[] { fieldNumber }, sfv);
293: smAttached.makeDirty(fieldNumber);
294: }
295: }
296:
297: /*
298: * (non-Javadoc)
299: * @see FieldConsumer#storeFloatField(int, float)
300: */
301: public void storeFloatField(int fieldNumber, float value) {
302: if (dirtyFields[fieldNumber] || !persistent) {
303: SingleValueFieldManager sfv = new SingleValueFieldManager();
304: sfv.storeFloatField(fieldNumber, value);
305: smAttached.replaceFields(new int[] { fieldNumber }, sfv);
306: smAttached.makeDirty(fieldNumber);
307: }
308: }
309:
310: /*
311: * (non-Javadoc)
312: * @see FieldConsumer#storeIntField(int, int)
313: */
314: public void storeIntField(int fieldNumber, int value) {
315: if (dirtyFields[fieldNumber] || !persistent) {
316: SingleValueFieldManager sfv = new SingleValueFieldManager();
317: sfv.storeIntField(fieldNumber, value);
318: smAttached.replaceFields(new int[] { fieldNumber }, sfv);
319: smAttached.makeDirty(fieldNumber);
320: }
321: }
322:
323: /*
324: * (non-Javadoc)
325: * @see FieldConsumer#storeLongField(int, long)
326: */
327: public void storeLongField(int fieldNumber, long value) {
328: if (dirtyFields[fieldNumber] || !persistent) {
329: SingleValueFieldManager sfv = new SingleValueFieldManager();
330: sfv.storeLongField(fieldNumber, value);
331: smAttached.replaceFields(new int[] { fieldNumber }, sfv);
332: smAttached.makeDirty(fieldNumber);
333: }
334: }
335:
336: /*
337: * (non-Javadoc)
338: * @see FieldConsumer#storeShortField(int, short)
339: */
340: public void storeShortField(int fieldNumber, short value) {
341: if (dirtyFields[fieldNumber] || !persistent) {
342: SingleValueFieldManager sfv = new SingleValueFieldManager();
343: sfv.storeShortField(fieldNumber, value);
344: smAttached.replaceFields(new int[] { fieldNumber }, sfv);
345: smAttached.makeDirty(fieldNumber);
346: }
347: }
348:
349: /*
350: * (non-Javadoc)
351: * @see FieldConsumer#storeStringField(int, java.lang.String)
352: */
353: public void storeStringField(int fieldNumber, String value) {
354: if (dirtyFields[fieldNumber] || !persistent) {
355: SingleValueFieldManager sfv = new SingleValueFieldManager();
356: sfv.storeStringField(fieldNumber, value);
357: smAttached.replaceFields(new int[] { fieldNumber }, sfv);
358: smAttached.makeDirty(fieldNumber);
359: }
360: }
361: }
|