001: /*
002: * Copyright (c) 1998 - 2005 Versant Corporation
003: * All rights reserved. This program and the accompanying materials
004: * are made available under the terms of the Eclipse Public License v1.0
005: * which accompanies this distribution, and is available at
006: * http://www.eclipse.org/legal/epl-v10.html
007: *
008: * Contributors:
009: * Versant Corporation - initial API and implementation
010: */
011: package com.versant.core.jdo;
012:
013: import com.versant.core.jdo.sco.*;
014: import com.versant.core.jdo.sco.detached.DetachSCOFactoryRegistry;
015: import com.versant.core.metadata.*;
016: import com.versant.core.common.OID;
017: import com.versant.core.common.State;
018: import com.versant.core.common.*;
019:
020: import javax.jdo.spi.JDOImplHelper;
021: import javax.jdo.spi.PersistenceCapable;
022: import java.lang.reflect.Array;
023: import java.util.HashMap;
024: import java.util.Map;
025:
026: /**
027: * This keeps track of State's, FetchGroup's, OID's and PCs for the detach
028: * code. It provides a list of State's and FetchGroup's for a breadth first
029: * traversal of a State graph. It also provides hashed access to the State
030: * and PC for a given OID.
031: */
032: public class DetachStateContainer {
033:
034: private static final int INITIAL_SIZE = 16;
035: private final JDOImplHelper jdoImplHelper = JDOImplHelper
036: .getInstance();
037:
038: private int size;
039: private int sizeFG;
040: private int sizeRefs;
041: private int capacity = INITIAL_SIZE;
042: private int capacityFG = INITIAL_SIZE;
043: private int capacityRefs = INITIAL_SIZE * 2;
044: private int posFG = -1;
045:
046: private State[] states = new State[capacity];
047: private PersistenceCapable[] pcs = new PersistenceCapable[capacity];
048: private OID[] oids = new OID[capacity];
049: private FetchGroup[][] stateFG = new FetchGroup[capacity][];
050: private FetchGroup[] fetchGroups = new FetchGroup[capacityFG];
051: private int[] stateIndexs = new int[capacityFG];
052: private State[] stateRefs = new State[capacityRefs];
053: private Map oidMap = new HashMap();
054:
055: private final VersantDetachStateManager stateManager = new VersantDetachStateManager();
056: private final VersantDetachedStateManager detachedStateManager = new VersantDetachedStateManager();
057: private final VersantPersistenceManagerImp pm;
058: private final DetachSCOFactoryRegistry scoFactoryRegistry = new DetachSCOFactoryRegistry();
059: private final ModelMetaData modelMetaData;
060:
061: public DetachStateContainer(VersantPersistenceManagerImp pm) {
062: this .pm = pm;
063: modelMetaData = pm.modelMetaData;
064: stateManager.setDsc(this );
065: }
066:
067: /**
068: * Add a oid and state to the container. If this oid is allready in the
069: * container we just copy the values from the new state into the container's
070: * state.
071: * The state can later be looked up in by using the oid.
072: */
073: public int add(OID oid, State newState) {
074: addState(newState); //keep a ref to newState so it will not be gc'ed out of the cache
075: Integer integer = (Integer) oidMap.get(oid);
076: if (integer == null) {
077: // we do not have a ref to this state yet
078: if (size == capacity) {
079: // make space for the new entry and copy it in
080: capacity = (capacity * 3) / 2 + 1;
081: State[] ns = new State[capacity];
082: System.arraycopy(states, 0, ns, 0, size);
083: states = ns;
084: PersistenceCapable[] npcs = new PersistenceCapable[capacity];
085: System.arraycopy(pcs, 0, npcs, 0, size);
086: pcs = npcs;
087: OID[] noids = new OID[capacity];
088: System.arraycopy(oids, 0, noids, 0, size);
089: oids = noids;
090: FetchGroup[][] nStateFG = new FetchGroup[capacity][];
091: System.arraycopy(stateFG, 0, nStateFG, 0, size);
092: stateFG = nStateFG;
093: }
094: states[size] = newState;
095: oids[size] = oid;
096: oidMap.put(oid, new Integer(size));
097: return size++;
098: } else {
099: // copy the values into the state we've allready got
100: int i = integer.intValue();
101: State realState = states[i];
102: if (realState == null) {
103: states[i] = newState;
104: } else if (realState != newState) {
105: if (newState != null) {
106: realState.updateNonFilled(newState);
107: }
108: newState = realState;
109: }
110: return i;
111: }
112: }
113:
114: /**
115: * Link the FetchGroup to this state.
116: */
117: public void add(OID oid, State newState, FetchGroup fg) {
118: int stateIndex = add(oid, newState);
119: if (fg == null)
120: return;
121: if (newState != null) {
122: fg = fg.resolve(newState.getClassMetaData(modelMetaData));
123: }
124: for (FetchGroup super FG = fg; super FG != null; super FG = super FG.super FetchGroup) {
125: addImpl(stateIndex, super FG);
126: }
127: }
128:
129: public void addImpl(int stateIndex, FetchGroup fg) {
130: FetchGroup[] sfg = stateFG[stateIndex];
131: if (sfg == null) {
132: sfg = new FetchGroup[4];
133: stateFG[stateIndex] = sfg;
134: }
135: int length = sfg.length;
136: boolean add = true;
137: for (int i = 0; i < length; i++) {
138: FetchGroup fetchGroup = sfg[i];
139: if (fetchGroup == fg) {
140: // we have this fetchGroup allready
141: return;
142: } else if (fetchGroup == null) {
143: // we have space so add the fetchGroup
144: sfg[i] = fg;
145: add = false;
146: break;
147: }
148: }
149: if (add) {
150: // make space for the new fetchGroup and copy it in
151: int oldSize = sfg.length;
152: int newSize = oldSize * 2;
153: FetchGroup[] nfg = new FetchGroup[newSize];
154: System.arraycopy(sfg, 0, nfg, 0, oldSize);
155: sfg = nfg;
156: sfg[oldSize] = fg;
157: stateFG[stateIndex] = sfg;
158: }
159: if (sizeFG == capacityFG) {
160: // make space for the new entry and copy it in
161: capacityFG = (capacityFG * 3) / 2 + 1;
162: FetchGroup[] ng = new FetchGroup[capacityFG];
163: System.arraycopy(fetchGroups, 0, ng, 0, sizeFG);
164: fetchGroups = ng;
165: int[] nsi = new int[capacityFG];
166: System.arraycopy(stateIndexs, 0, nsi, 0, sizeFG);
167: stateIndexs = nsi;
168: }
169: fetchGroups[sizeFG] = fg;
170: stateIndexs[sizeFG] = stateIndex;
171: sizeFG++;
172: }
173:
174: /**
175: * We keep a ref to these states so they will not be gc'ed out of the cache
176: */
177: private void addState(State newState) {
178: // make space for the new entry and copy in newState
179: if (sizeRefs == capacityRefs) {
180: capacityRefs = (capacityRefs * 3) / 2 + 1;
181: State[] ns = new State[capacityRefs];
182: System.arraycopy(stateRefs, 0, ns, 0, sizeRefs);
183: stateRefs = ns;
184: }
185: stateRefs[sizeRefs++] = newState;
186: }
187:
188: /**
189: * todo
190: */
191: public void createPcClasses(ModelMetaData jmd) {
192: if (pcs[0] != null)
193: return;
194: for (int i = 0; i < size; i++) {
195: PersistenceCapable pc = jdoImplHelper.newInstance(states[i]
196: .getClassMetaData(jmd).cls, stateManager);
197: if (!(pc instanceof VersantDetachable)) {
198: throw BindingSupportImpl
199: .getInstance()
200: .runtime(
201: "'"
202: + pc.getClass().getName()
203: + "' is not detachable please enhance "
204: + "classes with the detach option set to true");
205: }
206: VersantDetachable detachable = (VersantDetachable) pc;
207: Object externalOID = pm.getExternalOID(oids[i]);
208: oidMap.put(externalOID, new Integer(i));
209: detachable.versantSetOID(externalOID);
210: detachable.versantSetVersion(states[i]
211: .getOptimisticLockingValue());
212: pcs[i] = detachable;
213: }
214: for (int i = 0; i < size; i++) {
215: PersistenceCapable pc = pcs[i];
216: ClassMetaData cmd = states[i].getClassMetaData(jmd);
217: FieldMetaData[] pkFields = cmd.top.pkFields;
218: if (pkFields != null) {
219: for (int j = 0; j < pkFields.length; j++) {
220: FieldMetaData fmd = cmd.pkFields[j];
221: int managedFieldNo = fmd.managedFieldNo;
222: if (!stateManager.isLoaded(pc, managedFieldNo)) {
223: pc.jdoReplaceField(managedFieldNo);
224: }
225:
226: }
227: }
228: }
229: for (int i = 0; i < size; i++) {
230: PersistenceCapable pc = pcs[i];
231: FetchGroup[] sfg = stateFG[i];
232: int sfgLength = sfg.length;
233: for (int cfg = 0; cfg < sfgLength; cfg++) {
234: FetchGroup fg = sfg[cfg];
235: if (fg == null) {
236: break;
237: }
238: boolean defaultFG = fg == fg.classMetaData.fetchGroups[0];
239: for (int j = 0; j < fg.fields.length; j++) {
240: FetchGroupField field = fg.fields[j];
241: FieldMetaData fmd = field.fmd;
242: if (fmd.fake)
243: continue;
244: if (fmd.scoField)
245: continue;
246: if (defaultFG && !fmd.isJDODefaultFetchGroup())
247: continue;
248: int managedFieldNo = fmd.managedFieldNo;
249: if (stateManager.isLoaded(pc, managedFieldNo))
250: continue;
251: pc.jdoReplaceField(managedFieldNo);
252: }
253: }
254: }
255: for (int i = 0; i < size; i++) {
256: PersistenceCapable pc = pcs[i];
257: FetchGroup[] sfg = stateFG[i];
258: int sfgLength = sfg.length;
259: for (int cfg = 0; cfg < sfgLength; cfg++) {
260: FetchGroup fg = sfg[cfg];
261: if (fg == null) {
262: break;
263: }
264: boolean defaultFG = fg == fg.classMetaData.fetchGroups[0];
265: for (int j = 0; j < fg.fields.length; j++) {
266: FetchGroupField field = fg.fields[j];
267: FieldMetaData fmd = field.fmd;
268: if (fmd.fake)
269: continue;
270: if (!fmd.scoField)
271: continue;
272: if (defaultFG && !fmd.isJDODefaultFetchGroup())
273: continue;
274: int managedFieldNo = fmd.managedFieldNo;
275: if (stateManager.isLoaded(pc, managedFieldNo))
276: continue;
277: pc.jdoReplaceField(managedFieldNo);
278: }
279: }
280: }
281: for (int i = 0; i < size; i++) {
282: pcs[i].jdoReplaceStateManager(detachedStateManager);
283: }
284: }
285:
286: /**
287: * Are there more FetchGroups to be processed?
288: */
289: public boolean hasNextFetchGroup() {
290: posFG++;
291: return posFG < sizeFG;
292: }
293:
294: /**
295: * Get the state for the current fetch group.
296: */
297: public State getNextFetchGroupState() {
298: return states[stateIndexs[posFG]];
299: }
300:
301: /**
302: * Get the fetch group.
303: */
304: public FetchGroup getNextFetchGroup() {
305: return fetchGroups[posFG];
306: }
307:
308: /**
309: * Get the OID for the current fetch group.
310: */
311: public OID getNextFetchGroupOID() {
312: return oids[stateIndexs[posFG]];
313: }
314:
315: /**
316: * Do we have stuff for the oid?
317: */
318: public boolean contains(OID oid) {
319: if (oid == null)
320: return false;
321: return oidMap.containsKey(oid);
322: }
323:
324: /**
325: * Get the PC for the oid.
326: */
327: public PersistenceCapable getPC(OID oid) {
328: return pcs[((Integer) oidMap.get(oid)).intValue()];
329: }
330:
331: public State getState(PersistenceCapable pc) {
332: Object oid = ((VersantDetachable) pc).versantGetOID();
333: Integer integer = (Integer) oidMap.get(oid);
334: if (integer == null) {
335: return null;
336: }
337: return states[integer.intValue()];
338: }
339:
340: public Object getObjectField(VersantDetachable pc, int fieldNo) {
341: State state = getState(pc);
342: ClassMetaData cmd = state.getClassMetaData();
343: FieldMetaData fmd = cmd.stateFields[cmd.absToRel[fieldNo]];
344: int category = fmd.category;
345: Object o = state.getInternalObjectFieldAbs(fieldNo);
346:
347: switch (category) {
348: case MDStatics.CATEGORY_COLLECTION:
349: if (o != null) {
350: VersantSCOCollectionFactory factory = scoFactoryRegistry
351: .getJDOGenieSCOCollectionFactory(fmd);
352: if (o instanceof VersantSCOCollection) {
353: CollectionData collectionData = new CollectionData();
354: ((VersantSCOCollection) o)
355: .fillCollectionData(collectionData);
356: collectionData.valueCount = getDetachCopy(
357: collectionData.values,
358: collectionData.valueCount);
359: return factory.createSCOCollection(pc, pm,
360: detachedStateManager, fmd, collectionData);
361: } else {
362: CollectionData collectionData = new CollectionData();
363: Object[] values = (Object[]) o;
364: int length = values.length;
365: collectionData.values = new Object[length];
366: System.arraycopy(values, 0, collectionData.values,
367: 0, length);
368: collectionData.valueCount = collectionData.values.length;
369: collectionData.valueCount = getDetachCopy(
370: collectionData.values,
371: collectionData.valueCount);
372: return factory.createSCOCollection(pc, pm,
373: detachedStateManager, fmd, collectionData);
374: }
375: }
376: break;
377: case MDStatics.CATEGORY_ARRAY:
378: if (o != null) {
379: if (!o.getClass().isArray())
380: return o;
381: Class type = o.getClass().getComponentType();
382: int length = Array.getLength(o);
383: Object newArray = Array.newInstance(type, length);
384: System.arraycopy(o, 0, newArray, 0, length);
385: if (fmd.isElementTypePC()) {
386: getDetachCopy((Object[]) newArray, length);
387: }
388: return newArray;
389: }
390: break;
391: case MDStatics.CATEGORY_MAP:
392: if (o != null) {
393: VersantSCOMapFactory factory = scoFactoryRegistry
394: .getJDOGenieSCOMapFactory(fmd);
395: if (o instanceof VersantSCOMap) {
396: MapData mapData = new MapData();
397: ((VersantSCOMap) o).fillMapData(mapData);
398: mapData.entryCount = getDetachCopy(mapData.keys,
399: mapData.entryCount);
400: mapData.entryCount = getDetachCopy(mapData.values,
401: mapData.entryCount);
402: return factory.createSCOHashMap(pc, pm,
403: detachedStateManager, fmd, mapData);
404: } else {
405: MapEntries entries = (MapEntries) o;
406: MapData mapData = new MapData();
407: mapData.entryCount = entries.keys.length;
408: Object[] keys = new Object[mapData.entryCount];
409: System.arraycopy(entries.keys, 0, keys, 0,
410: mapData.entryCount);
411: Object[] values = new Object[mapData.entryCount];
412: System.arraycopy(entries.values, 0, values, 0,
413: mapData.entryCount);
414: mapData.keys = keys;
415: mapData.values = values;
416: mapData.entryCount = getDetachCopy(mapData.keys,
417: mapData.entryCount);
418: mapData.entryCount = getDetachCopy(mapData.values,
419: mapData.entryCount);
420: return factory.createSCOHashMap(pc, pm,
421: detachedStateManager, fmd, mapData);
422: }
423: }
424: break;
425: case MDStatics.CATEGORY_EXTERNALIZED:
426: if (o != null) {
427: if (state.isResolvedForClient(fieldNo)) {
428: o = fmd.externalizer.toExternalForm(pm, o);
429: }
430: o = fmd.externalizer.fromExternalForm(pm, o);
431: }
432: return o;
433: case MDStatics.CATEGORY_REF:
434: case MDStatics.CATEGORY_POLYREF:
435: if (o != null) {
436: if (fmd.scoField) {
437: VersantSCOFactory factory = scoFactoryRegistry
438: .getJdoGenieSCOFactory(fmd);
439: return factory.createSCO(pc, pm,
440: detachedStateManager, fmd, o);
441: } else {
442: return getDetachCopy(o);
443: }
444: }
445: break;
446: }
447: return o;
448: }
449:
450: public PersistenceCapable getDetachCopy(OID oid) {
451: Integer integer = (Integer) oidMap.get(oid);
452: int index = integer == null ? -1 : integer.intValue();
453: if (index < 0) {
454: throw BindingSupportImpl.getInstance().exception(
455: "Could not find detached copy of object with oid='"
456: + oid + "'");
457: }
458: return pcs[index];
459: }
460:
461: public PersistenceCapable getDetachCopy(PersistenceCapable pc) {
462: PCStateMan internalSM = pm.getInternalSM(pc);
463: return getDetachCopy(internalSM.oid);
464: }
465:
466: public int getDetachCopy(Object[] objects, int count) {
467: count = Math.min(count, objects.length);
468: for (int i = 0; i < count; i++) {
469: Object o = objects[i];
470: if (o != null) {
471: objects[i] = getDetachCopy(o);
472: } else {
473: return i;
474: }
475: }
476: return count;
477: }
478:
479: public Object getDetachCopy(Object o) {
480: if (o instanceof OID) {
481: return getDetachCopy((OID) o);
482: } else if (o instanceof PersistenceCapable) {
483: return getDetachCopy((PersistenceCapable) o);
484: }
485: return o;
486: }
487:
488: public VersantDetachStateManager getStateManager() {
489: return stateManager;
490: }
491: }
|