blob: 776de660275bebf9d250c5fbc4a701898d27d82b [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.openjpa.kernel;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.Serializable;
import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Date;
import java.util.Map;
import java.util.TimeZone;
import org.apache.openjpa.enhance.PersistenceCapable;
import org.apache.openjpa.lib.util.Localizer;
import org.apache.openjpa.meta.FieldMetaData;
import org.apache.openjpa.meta.JavaTypes;
import org.apache.openjpa.meta.ValueMetaData;
import org.apache.openjpa.meta.ValueStrategies;
import org.apache.openjpa.util.ChangeTracker;
import org.apache.openjpa.util.Exceptions;
import org.apache.openjpa.util.ImplHelper;
import org.apache.openjpa.util.InvalidStateException;
import org.apache.openjpa.util.LRSProxy;
import org.apache.openjpa.util.MapChangeTracker;
import org.apache.openjpa.util.ObjectId;
import org.apache.openjpa.util.Proxies;
import org.apache.openjpa.util.Proxy;
import org.apache.openjpa.util.ProxyManager;
import org.apache.openjpa.util.UserException;
/**
* FieldManager type used to hold onto a single field value and then
* dispense it via the fetch methods. The manager can also perform actions
* on the held field.
*/
class SingleFieldManager extends TransferFieldManager implements Serializable {
private static final long serialVersionUID = 1L;
private static final Localizer _loc = Localizer.forPackage(SingleFieldManager.class);
private final StateManagerImpl _sm;
private final BrokerImpl _broker;
private final boolean _checkDbOnCascadePersist;
public SingleFieldManager(StateManagerImpl sm, BrokerImpl broker) {
_sm = sm;
_broker = broker;
_checkDbOnCascadePersist =
_broker.getConfiguration().getCompatibilityInstance().getCheckDatabaseForCascadePersistToDetachedEntity();
}
/**
* Proxy the held field if needed. Return true if the field needs to
* be replaced with the now-proxied instance.
*/
public boolean proxy(boolean reset, boolean replaceNull) {
FieldMetaData fmd = _sm.getMetaData().getField(field);
Proxy proxy = null;
boolean ret = false;
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.DATE:
if (objval == null)
return false;
proxy = checkProxy(fmd);
if (proxy == null) {
proxy = (Proxy) _sm.newFieldProxy(field);
((Date) proxy).setTime(((Date) objval).getTime());
if (proxy instanceof Timestamp && objval instanceof Timestamp)
((Timestamp) proxy).setNanos(((Timestamp) objval).getNanos());
ret = true;
}
break;
case JavaTypes.CALENDAR:
if (objval == null)
return false;
proxy = checkProxy(fmd);
if (proxy == null) {
proxy = (Proxy) _sm.newFieldProxy(field);
((Calendar) proxy).setTime(((Calendar) objval).getTime());
ret = true;
} else {
Object init = fmd.getInitializer();
if (init != null && init instanceof TimeZone) {
((Calendar) proxy).setTimeZone((TimeZone)init);
}
}
break;
case JavaTypes.COLLECTION:
if (objval == null && !replaceNull)
return false;
proxy = checkProxy(fmd);
if (proxy == null) {
proxy = (Proxy) _sm.newFieldProxy(field);
if (objval != null)
((Collection) proxy).addAll((Collection) objval);
ret = true;
}
break;
case JavaTypes.MAP:
if (objval == null && !replaceNull)
return false;
proxy = checkProxy(fmd);
if (proxy == null) {
proxy = (Proxy) _sm.newFieldProxy(field);
if (objval != null)
((Map) proxy).putAll((Map) objval);
ret = true;
}
break;
case JavaTypes.OBJECT:
if (objval == null)
return false;
proxy = checkProxy(fmd);
if (proxy == null) {
proxy = getProxyManager().newCustomProxy(objval,
_sm.getBroker().getConfiguration().
getCompatibilityInstance().getAutoOff());
ret = proxy != null;
}
break;
}
if (proxy != null) {
proxy.setOwner(_sm, field);
ChangeTracker tracker = proxy.getChangeTracker();
if (reset && tracker != null) {
if (fmd.getDeclaredTypeCode() == JavaTypes.MAP) {
// track values if key is derived from value, else keys
boolean keys = fmd.getKey().getValueMappedBy() == null;
((MapChangeTracker) tracker).setTrackKeys(keys);
}
tracker.startTracking();
}
objval = proxy;
}
return ret;
}
/**
* If the current field is a usable proxy and it should be a proxy, return it; else return null.
*
* This method will skim out Calendar instances that were proxied before we knew if they need to be proxied.
*/
private Proxy checkProxy(FieldMetaData fmd) {
if (!(objval instanceof Proxy))
return null;
Proxy proxy = (Proxy) objval;
if (proxy.getOwner() == null || Proxies.isOwner(proxy, _sm, field)) {
if (fmd.getProxyType().isAssignableFrom(proxy.getClass())
|| (fmd.isLRS() && (objval instanceof LRSProxy))) {
return proxy;
}
}
return null;
}
/**
* Unproxies the current field if needed.
*/
public void unproxy() {
if (objval == null)
return;
FieldMetaData fmd = _sm.getMetaData().getField(field);
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.COLLECTION:
case JavaTypes.MAP:
case JavaTypes.DATE:
case JavaTypes.OBJECT:
if (objval instanceof Proxy) {
Proxy proxy = (Proxy) objval;
proxy.setOwner(null, -1);
if (proxy.getChangeTracker() != null)
proxy.getChangeTracker().stopTracking();
}
}
}
/**
* Release the currently embedded field (make it transient).
*/
public void releaseEmbedded() {
if (objval == null)
return;
FieldMetaData fmd = _sm.getMetaData().getField(field);
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.PC:
if (fmd.isEmbeddedPC())
releaseEmbedded(fmd, objval);
break;
case JavaTypes.ARRAY:
if (fmd.getElement().isEmbeddedPC())
releaseEmbedded(fmd.getElement(), (Object[]) objval);
break;
case JavaTypes.COLLECTION:
if (fmd.getElement().isEmbeddedPC())
releaseEmbedded(fmd.getElement(), (Collection) objval);
break;
case JavaTypes.MAP:
if (fmd.getKey().isEmbeddedPC())
releaseEmbedded(fmd.getKey(), ((Map) objval).keySet());
if (fmd.getElement().isEmbeddedPC())
releaseEmbedded(fmd.getElement(), ((Map) objval).values());
break;
}
}
/**
* Release the given embedded objects.
*/
private void releaseEmbedded(ValueMetaData vmd, Object[] objs) {
for (Object obj : objs) {
releaseEmbedded(vmd, obj);
}
}
/**
* Release the given embedded objects.
*/
private void releaseEmbedded(ValueMetaData vmd, Collection objs) {
for (Object obj : objs) {
releaseEmbedded(vmd, obj);
}
}
/**
* Release the given embedd object.
*/
private void releaseEmbedded(ValueMetaData vmd, Object obj) {
if (obj == null)
return;
StateManagerImpl sm = _broker.getStateManagerImpl(obj, false);
if (sm != null && sm.getOwner() == _sm
&& sm.getOwnerIndex() == vmd.getFieldMetaData().getIndex())
sm.release(true);
}
/**
* Persist the stored field safely, preventing infinite recursion using
* the given set of already-persisted objects. This method is only called
* for fields that we know have cascade-immediate settings.
*/
public void persist(OpCallbacks call) {
if (objval == null)
return;
FieldMetaData fmd = _sm.getMetaData().getField(field);
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.PC:
case JavaTypes.PC_UNTYPED:
if (!_broker.isDetachedNew() && _broker.isDetached(objval, _checkDbOnCascadePersist))
return; // allow but ignore
_broker.persist(objval, true, call);
break;
case JavaTypes.ARRAY:
_broker.persistAll(Arrays.asList((Object[]) objval), true,
call);
break;
case JavaTypes.COLLECTION:
_broker.persistAll((Collection) objval, true, call);
break;
case JavaTypes.MAP:
if (fmd.getKey().getCascadePersist()
== ValueMetaData.CASCADE_IMMEDIATE)
_broker.persistAll(((Map) objval).keySet(), true, call);
if (fmd.getElement().getCascadePersist()
== ValueMetaData.CASCADE_IMMEDIATE)
_broker.persistAll(((Map) objval).values(), true, call);
break;
}
}
/**
* Delete and/or dereference field values.
*/
public void delete(OpCallbacks call) {
delete(true, call);
}
/**
* Dereference field values.
*/
public void dereferenceDependent() {
delete(false, null, true);
}
private void delete(boolean immediate, OpCallbacks call) {
delete(immediate, call, false);
}
/**
* Delete or dereference the stored field as necessary.
*/
private void delete(boolean immediate, OpCallbacks call, boolean deref) {
if (objval == null)
return;
FieldMetaData fmd = _sm.getMetaData().getField(field);
if (fmd.getCascadeDelete() != ValueMetaData.CASCADE_NONE) {
// immediate cascade works on field value; dependent deref
// works on external value
if ((immediate || fmd.isEmbeddedPC())
&& fmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE) {
if (fmd.isEmbeddedPC() && deref) {
StateManagerImpl sm = _broker.getStateManagerImpl(objval, false);
if (sm != null)
dereferenceEmbedDependent(sm);
}
delete(fmd, objval, call);
}
else if (fmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO)
dereferenceDependent(fmd.getExternalValue(objval, _broker));
return;
}
Object external = null;
ValueMetaData vmd = fmd.getKey();
if ((immediate || vmd.isEmbeddedPC())
&& vmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE)
delete(vmd, ((Map) objval).keySet(), call);
else if (vmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO) {
external = fmd.getExternalValue(objval, _broker);
if (external == null)
return;
dereferenceDependent(((Map) external).keySet());
}
vmd = fmd.getElement();
if ((immediate || vmd.isEmbeddedPC())
&& vmd.getCascadeDelete() == ValueMetaData.CASCADE_IMMEDIATE) {
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.COLLECTION:
delete(vmd, (Collection) objval, call);
break;
case JavaTypes.ARRAY:
delete(vmd, (Object[]) objval, call);
break;
case JavaTypes.MAP:
delete(vmd, ((Map) objval).values(), call);
break;
}
} else if (vmd.getCascadeDelete() == ValueMetaData.CASCADE_AUTO) {
if (external == null) {
external = fmd.getExternalValue(objval, _broker);
if (external == null)
return;
}
switch (fmd.getTypeCode()) {
case JavaTypes.COLLECTION:
dereferenceDependent((Collection) external);
break;
case JavaTypes.ARRAY:
dereferenceDependent((Object[]) external);
break;
case JavaTypes.MAP:
dereferenceDependent(((Map) external).values());
break;
}
}
}
/**
* Delete the objects in the given value.
*/
private void delete(ValueMetaData vmd, Object[] objs, OpCallbacks call) {
for (Object obj : objs) {
delete(vmd, obj, call);
}
}
/**
* Delete the objects embedded in the given value.
*/
private void delete(ValueMetaData vmd, Collection objs, OpCallbacks call) {
for (Object obj : objs) {
delete(vmd, obj, call);
}
}
/**
* Delete an object embedded in the given value.
*/
void delete(ValueMetaData vmd, Object obj, OpCallbacks call) {
if (obj == null)
return;
// delete if unknowned or this isn't an embedded field or if owned by us
StateManagerImpl sm = _broker.getStateManagerImpl(obj, false);
if (sm != null && (sm.getOwner() == null || !vmd.isEmbeddedPC()
|| (sm.getOwner() == _sm
&& sm.getOwnerIndex() == vmd.getFieldMetaData().getIndex())))
_broker.delete(sm.getManagedInstance(), sm, call);
}
/**
* Dereference all valid persistent objects in the given collection.
*/
private void dereferenceDependent(Object[] objs) {
for (Object obj : objs) {
dereferenceDependent(obj);
}
}
/**
* Dereference all valid persistent objects in the given collection.
*/
private void dereferenceDependent(Collection objs) {
for (Object obj : objs) {
dereferenceDependent(obj);
}
}
/**
* Dereference the given object.
*/
void dereferenceDependent(Object obj) {
if (obj == null)
return;
StateManagerImpl sm = _broker.getStateManagerImpl(obj, false);
if (sm != null)
sm.setDereferencedDependent(true, true);
}
void dereferenceEmbedDependent(StateManagerImpl sm) {
sm.setDereferencedEmbedDependent(true);
}
/**
* Recursively invoke the broker to gather cascade-refresh objects in
* the current field into the given set. This method is only called
* for fields that we know have cascade-refresh settings.
*/
public void gatherCascadeRefresh(OpCallbacks call) {
if (objval == null)
return;
FieldMetaData fmd = _sm.getMetaData().getField(field);
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.PC:
case JavaTypes.PC_UNTYPED:
_broker.gatherCascadeRefresh(objval, call);
break;
case JavaTypes.ARRAY:
gatherCascadeRefresh((Object[]) objval, call);
break;
case JavaTypes.COLLECTION:
gatherCascadeRefresh((Collection) objval, call);
break;
case JavaTypes.MAP:
if (fmd.getKey().getCascadeRefresh()
== ValueMetaData.CASCADE_IMMEDIATE)
gatherCascadeRefresh(((Map) objval).keySet(), call);
if (fmd.getElement().getCascadeRefresh()
== ValueMetaData.CASCADE_IMMEDIATE)
gatherCascadeRefresh(((Map) objval).values(), call);
break;
}
}
/**
* Gather each element.
*/
private void gatherCascadeRefresh(Object[] arr, OpCallbacks call) {
for (Object o : arr) {
_broker.gatherCascadeRefresh(o, call);
}
}
/**
* Gather each element.
*/
private void gatherCascadeRefresh(Collection coll, OpCallbacks call) {
for (Object o : coll) {
_broker.gatherCascadeRefresh(o, call);
}
}
/**
* Perform pre-flush tasks on the current field. This includes checking
* for nulls, persisting pcs, embedding embedded fields, and ref'ing
* pc fields. Return true if the field needs to be replaced with the
* new value.
*/
public boolean preFlush(boolean logical, OpCallbacks call) {
// only care about object fields
FieldMetaData fmd = _sm.getMetaData().getField(field);
if (fmd.getDeclaredTypeCode() < JavaTypes.OBJECT)
return false;
// manage inverses
InverseManager manager = _broker.getInverseManager();
if (manager != null)
manager.correctRelations(_sm, fmd, objval);
// perform pers-by-reach and dependent refs
return preFlush(fmd, logical, call);
}
/**
* Return true if the last-provided field has a default value.
*/
public boolean isDefaultValue() {
return dblval == 0 && longval == 0
&& (objval == null || "".equals(objval));
}
/**
* Write the stored field or its default value to the given stream.
*/
public void serialize(ObjectOutput out, boolean def)
throws IOException {
FieldMetaData fmd = _sm.getMetaData().getField(field);
switch (fmd.getDeclaredTypeCode()) {
case JavaTypes.BOOLEAN:
out.writeBoolean(!def && longval == 1);
break;
case JavaTypes.BYTE:
out.writeByte((def) ? (byte) 0 : (byte) longval);
break;
case JavaTypes.CHAR:
out.writeChar((def) ? (char) 0 : (char) longval);
break;
case JavaTypes.DOUBLE:
out.writeDouble((def) ? 0D : dblval);
break;
case JavaTypes.FLOAT:
out.writeFloat((def) ? 0F : (float) dblval);
break;
case JavaTypes.INT:
out.writeInt((def) ? 0 : (int) longval);
break;
case JavaTypes.LONG:
out.writeLong((def) ? 0L : longval);
break;
case JavaTypes.SHORT:
out.writeShort((def) ? (short) 0 : (short) longval);
break;
default:
out.writeObject((def) ? null : objval);
}
}
/**
* Helper method to perform pre flush actions on the current object.
*/
private boolean preFlush(FieldMetaData fmd, boolean logical, OpCallbacks call) {
// check for illegal nulls
if (objval == null) {
// If we have an AUTOASSIGN strategy that means that we have a field that is GenerationType.IDENTITY so
// skip checking to see if the value is null as it will get assigned later in flush processing.
if (fmd.getValueStrategy() != ValueStrategies.AUTOASSIGN) {
if (fmd.getNullValue() == FieldMetaData.NULL_EXCEPTION || fmd.getDeclaredTypeCode() == JavaTypes.OID)
throw new InvalidStateException(_loc.get("null-value", fmd.getName(), _sm.getManagedInstance()))
.setFatal(true);
}
return false;
}
// nothing else to do for non-persistent
if (fmd.getManagement() != FieldMetaData.MANAGE_PERSISTENT)
return false;
// don't allow managed objectid field value
if (fmd.getDeclaredTypeCode() == JavaTypes.OID) {
_sm.assertNotManagedObjectId(objval);
if (_sm.getObjectId() != null
&& !objval.equals(((ObjectId) _sm.getObjectId()).getId()))
throw new InvalidStateException(_loc.get("changed-oid",
_sm.getObjectId(), objval,
Exceptions.toString(_sm.getManagedInstance()))).
setFatal(true);
}
// check for pcs in field value
if (preFlush(fmd, fmd.getDeclaredTypeCode(),
fmd.getKey().getDeclaredTypeCode(),
fmd.getElement().getDeclaredTypeCode(), false, logical, call))
return true;
// also check for pcs in externalized values
if (fmd.isExternalized())
preFlush(fmd, fmd.getTypeCode(), fmd.getKey().getTypeCode(),
fmd.getElement().getTypeCode(), true, logical, call);
return false;
}
/**
* Make new objects persistent and ref other objects so referenced
* dependent objects won't be deleted.
*/
private boolean preFlush(FieldMetaData fmd, int type, int keyType,
int elemType, boolean external, boolean logical, OpCallbacks call) {
Object val = objval;
if (val == null)
return false;
boolean copy = false;
switch (type) {
case JavaTypes.PC:
if (fmd.isEmbeddedPC()) {
objval = embed(fmd, val);
copy = true;
} else {
if (external)
val = fmd.getExternalValue(val, _broker);
if (val != null)
preFlushPC(fmd, val, logical, call);
}
break;
case JavaTypes.PC_UNTYPED:
if (external)
val = fmd.getExternalValue(val, _broker);
if (val != null)
preFlushPC(fmd, val, logical, call);
break;
case JavaTypes.ARRAY:
if (fmd.getElement().isEmbeddedPC())
embed(fmd.getElement(), (Object[]) val);
else if (elemType == JavaTypes.PC
|| elemType == JavaTypes.PC_UNTYPED) {
if (external)
val = fmd.getExternalValue(val, _broker);
if (val != null)
preFlushPCs(fmd.getElement(), (Object[]) val, logical,
call);
}
break;
case JavaTypes.COLLECTION:
if (fmd.getElement().isEmbeddedPC()) {
objval = embed(fmd.getElement(), (Collection) val);
copy = true;
} else if (elemType == JavaTypes.PC
|| elemType == JavaTypes.PC_UNTYPED) {
boolean flushed = false;
if (external)
val = fmd.getExternalValue(val, _broker);
else if (val instanceof Proxy) {
// shortcut change trackers; also ensures we don't
// iterate lrs fields
ChangeTracker ct = ((Proxy) val).getChangeTracker();
if (ct != null && ct.isTracking()) {
preFlushPCs(fmd.getElement(), ct.getAdded(),
logical, call);
preFlushPCs(fmd.getElement(), ct.getChanged(),
logical, call);
flushed = true;
}
}
if (!flushed && val != null)
preFlushPCs(fmd.getElement(), (Collection) val, logical,
call);
}
break;
case JavaTypes.MAP:
boolean keyEmbed = fmd.getKey().isEmbeddedPC();
boolean valEmbed = fmd.getElement().isEmbeddedPC();
if (keyEmbed || valEmbed) {
objval = embed(fmd, (Map) val, keyEmbed, valEmbed);
copy = keyEmbed;
}
if (!keyEmbed && (keyType == JavaTypes.PC
|| keyType == JavaTypes.PC_UNTYPED)) {
boolean flushed = false;
if (external) {
val = fmd.getExternalValue(val, _broker);
external = false;
} else if (val instanceof Proxy) {
// shortcut change trackers; also ensures we don't
// iterate lrs fields
MapChangeTracker ct = (MapChangeTracker) ((Proxy) val).
getChangeTracker();
if (ct != null && ct.isTracking() && ct.getTrackKeys())
{
preFlushPCs(fmd.getKey(), ct.getAdded(), logical,
call);
preFlushPCs(fmd.getKey(), ct.getChanged(), logical,
call);
flushed = true;
}
}
if (!flushed && val != null)
preFlushPCs(fmd.getKey(), ((Map) val).keySet(), logical,
call);
}
if (!valEmbed && (elemType == JavaTypes.PC
|| elemType == JavaTypes.PC_UNTYPED)) {
boolean flushed = false;
if (external)
val = fmd.getExternalValue(val, _broker);
else if (val instanceof Proxy) {
// shortcut change trackers; also ensures we don't
// iterate lrs fields
MapChangeTracker ct = (MapChangeTracker) ((Proxy) val).
getChangeTracker();
if (ct != null && ct.isTracking()) {
if (ct.getTrackKeys()) {
preFlushPCs(fmd.getElement(), ct.getAdded(),
(Map) val, logical, call);
preFlushPCs(fmd.getElement(), ct.getChanged(),
(Map) val, logical, call);
} else {
preFlushPCs(fmd.getElement(), ct.getAdded(),
logical, call);
preFlushPCs(fmd.getElement(), ct.getChanged(),
logical, call);
}
flushed = true;
}
}
if (!flushed && val != null)
preFlushPCs(fmd.getElement(), ((Map) val).values(),
logical, call);
}
break;
}
return copy;
}
/**
* Make new objects persistent and ref all valid persistent objects for
* the given keys.
*/
private void preFlushPCs(ValueMetaData vmd, Collection keys, Map map,
boolean logical, OpCallbacks call) {
for (Object key : keys) {
preFlushPC(vmd, map.get(key), logical, call);
}
}
/**
* Make new objects persistent and ref all valid persistent objects in
* the given array.
*/
private void preFlushPCs(ValueMetaData vmd, Object[] objs,
boolean logical, OpCallbacks call) {
for (Object obj : objs) {
preFlushPC(vmd, obj, logical, call);
}
}
/**
* Make new objects persistent and ref all valid persistent objects in
* the given collection.
*/
private void preFlushPCs(ValueMetaData vmd, Collection objs,
boolean logical, OpCallbacks call) {
for (Object obj : objs) {
preFlushPC(vmd, obj, logical, call);
}
}
/**
* Perform pre flush operations on the given object.
*/
private void preFlushPC(ValueMetaData vmd, Object obj, boolean logical, OpCallbacks call) {
if (obj == null)
return;
OpenJPAStateManager sm;
if (vmd.getCascadePersist() == ValueMetaData.CASCADE_NONE) {
if (!_broker.isDetachedNew() && _broker.isDetached(obj, _checkDbOnCascadePersist)) {
return; // allow but ignore
}
sm = _broker.getStateManager(obj);
if (sm == null || !sm.isPersistent()) {
if (((StoreContext)_broker).getAllowReferenceToSiblingContext()
&& ImplHelper.isManageable(obj)
&& ((PersistenceCapable)obj).pcGetStateManager() != null) {
return;
} else {
throw new InvalidStateException(_loc.get("cant-cascade-persist",
vmd.toString(), Exceptions.toString(obj),
sm == null ? " unmanaged" : sm.getPCState().getClass().getSimpleName()))
.setFailedObject(obj);
}
}
} else {
if (vmd.getCascadePersist() == ValueMetaData.CASCADE_IMMEDIATE) {
if (!_broker.isDetachedNew() && _broker.isDetached(obj, _checkDbOnCascadePersist)) {
return; // allow but ignore
}
}
sm = _broker.getStateManager(obj);
if (sm == null || !sm.isProvisional()) {
sm = _broker.persist(obj, null, true, call);
// ensure generated IDs get assigned properly
if (!logical)
((StateManagerImpl)sm).assignObjectId(false, true);
// Call preFetch on this and any related persistent fields.
// This will ensure IDs get assigned to those that need them.
if (_broker.isFlushing()) {
((StateManagerImpl)sm).preFlush(logical, call);
}
}
}
if (sm != null) {
// if deleted and not managed inverse, die
if (sm.isDeleted() && (_broker.getInverseManager() == null
|| vmd.getFieldMetaData().getInverseMetaDatas().length == 0))
throw new UserException(_loc.get("ref-to-deleted",
Exceptions.toString(obj), vmd,
Exceptions.toString(_sm.getManagedInstance()))).
setFailedObject(obj);
StateManagerImpl smimpl = (StateManagerImpl) sm;
smimpl.nonprovisional(logical, call);
smimpl.setDereferencedDependent(false, true);
}
}
/**
* Make all elements of the given array embedded.
*/
private void embed(ValueMetaData vmd, Object[] arr) {
for (int i = 0; i < arr.length; i++)
arr[i] = embed(vmd, arr[i]);
}
/**
* Create a copy of the given collection containing embedded elements.
*/
private Collection embed(ValueMetaData vmd, Collection orig) {
// we have to copy to get a collection of the right type and size,
// though we immediately clear it
if (orig == null)
throw new UserException(_loc.get("not-copyable",
vmd.getFieldMetaData()));
Collection coll = null;
try {
coll = getProxyManager().copyCollection(orig);
} catch (Exception e) {
coll = (Collection) _sm.newFieldProxy(vmd.getFieldMetaData().getIndex());
}
coll.clear();
for (Object o : orig) {
coll.add(embed(vmd, o));
}
return coll;
}
/**
* Embed the elements of the given map.
*/
private Map embed(FieldMetaData fmd, Map orig, boolean keyEmbed,
boolean valEmbed) {
Map map;
Map.Entry entry;
// if we have to replace keys, we need to copy the map; otherwise
// we can mutate the values directly
if (keyEmbed) {
if (orig == null)
throw new UserException(_loc.get("not-copyable", fmd));
// we have to copy to get a collection of the right type and size,
// though we immediately clear it
try {
map = getProxyManager().copyMap(orig);
} catch (Exception e) {
map = (Map) _sm.newFieldProxy(fmd.getIndex());
}
map.clear();
Object key, val;
for (Object o : orig.entrySet()) {
entry = (Map.Entry) o;
key = embed(fmd.getKey(), entry.getKey());
val = entry.getValue();
if (valEmbed)
val = embed(fmd.getElement(), val);
map.put(key, val);
}
} else {
map = orig;
for (Object o : map.entrySet()) {
entry = (Map.Entry) o;
entry.setValue(embed(fmd.getElement(),
entry.getValue()));
}
}
return map;
}
/**
* Make the given object embedded.
*/
private Object embed(ValueMetaData vmd, Object obj) {
if (obj == null)
return null;
return _broker.embed(obj, null, _sm, vmd).getManagedInstance();
}
/**
* Return the proxy manager.
*/
private ProxyManager getProxyManager ()
{
return _broker.getConfiguration ().getProxyManagerInstance ();
}
}