public Object attach(AttachManager manager, Object toAttach,
ClassMetaData meta, PersistenceCapable into, OpenJPAStateManager owner,
ValueMetaData ownerMeta, boolean explicit) {
BrokerImpl broker = manager.getBroker();
PersistenceCapable pc = ImplHelper.toPersistenceCapable(toAttach,
manager.getBroker().getConfiguration());
Object[] state = (Object[]) pc.pcGetDetachedState();
boolean embedded = ownerMeta != null && ownerMeta.isEmbeddedPC();
int offset;
StateManagerImpl sm;
// state == null means this is a new instance; also, if the
// state manager for the embedded instance is null, then
// it should be treated as a new instance (since the
// newly persisted owner may create a new embedded instance
// in the constructor); fixed bug #1075.
// also, if the user has attached a detached obj from somewhere
// else in the graph to an embedded field that was previously null,
// copy into a new embedded instance
if (embedded && (state == null || into == null
|| broker.getStateManager(into) == null)) {
if (into == null)
into = pc.pcNewInstance(null, false);
sm = (StateManagerImpl) broker.embed(into, null, owner, ownerMeta);
into = sm.getPersistenceCapable();
} else if (state == null) {
sm = persist(manager, pc, meta, ApplicationIds.create(pc, meta),
explicit);
into = sm.getPersistenceCapable();
} else if (!embedded && into == null) {
Object id = getDetachedObjectId(manager, pc);
if (id != null)
into =
ImplHelper.toPersistenceCapable(broker.find(id, true, null),
manager.getBroker().getConfiguration());
if (into == null) {
// we mark objects that were new on detach by putting an empty
// extra element in their detached state array
offset = meta.getIdentityType() == meta.ID_DATASTORE ? 1 : 0;
boolean isNew = state.length == 3 + offset;
// attempting to attach an instance that has been deleted
// will throw an OVE if it was not PNEW when it was detached
if (!isNew)
throw new OptimisticException(_loc.get("attach-deleted",
ImplHelper.getManagedInstance(pc).getClass(), id))
.setFailedObject(id);
// if the instance does not exist, we assume that it was
// made persistent in a new transaction, detached, and then
// the transaction was rolled back; the danger is that
// the instance was made persistent, detached, committed,
// and then deleted, but this is an uncommon case
sm = persist(manager, pc, meta, id, explicit);
into = sm.getPersistenceCapable();
// nullify the state, since the new instance won't have one
state = null;
} else
sm = manager.assertManaged(into);
} else
sm = manager.assertManaged(into);
// mark that we attached the instance *before* we
// fill in values to avoid endless recursion
manager.setAttachedCopy(pc, into);
meta = sm.getMetaData();
manager.fireBeforeAttach(pc, meta);
offset = meta.getIdentityType() == meta.ID_DATASTORE ? 1 : 0;
// assign the detached pc the same state manager as the object we're
// copying into during the attach process
pc.pcReplaceStateManager(sm);
BitSet fields = state == null ? null : (BitSet) state[1 + offset];
try {
FieldMetaData[] fmds = meta.getFields();
for (int i = 0; i < fmds.length; i++) {
// only attach fields in the FG of the detached instance; new
// instances get all their fields attached
if (fields == null || fields.get(i))
attachField(manager, pc, sm, fmds[i], true);
}
}
finally {
pc.pcReplaceStateManager(null);
}
// set the next version for non-new instances that are not embedded
if (state != null && !embedded) {
// make sure that all the fields in the original FG are loaded