use of org.apache.cayenne.reflect.PropertyVisitor in project cayenne by apache.
the class ObjectContextDeleteAction method processDeleteRules.
private void processDeleteRules(final Persistent object, int oldState) throws DeleteDenyException {
ClassDescriptor descriptor = context.getEntityResolver().getClassDescriptor(object.getObjectId().getEntityName());
for (final ObjRelationship relationship : descriptor.getEntity().getRelationships()) {
boolean processFlattened = relationship.isFlattened() && relationship.isToDependentEntity() && !relationship.isReadOnly();
// first check for no action... bail out if no flattened processing is needed
if (relationship.getDeleteRule() == DeleteRule.NO_ACTION && !processFlattened) {
continue;
}
ArcProperty property = (ArcProperty) descriptor.getProperty(relationship.getName());
final Collection<Persistent> relatedObjects = toCollection(property.readProperty(object));
// no related object, bail out
if (relatedObjects.size() == 0) {
continue;
}
// process DENY rule first...
if (relationship.getDeleteRule() == DeleteRule.DENY) {
object.setPersistenceState(oldState);
String message = relatedObjects.size() == 1 ? "1 related object" : relatedObjects.size() + " related objects";
throw new DeleteDenyException(object, relationship.getName(), message);
}
// object graph
if (processFlattened) {
for (Persistent relatedObject : relatedObjects) {
context.getGraphManager().arcDeleted(object.getObjectId(), relatedObject.getObjectId(), relationship.getName());
}
}
// process remaining rules
switch(relationship.getDeleteRule()) {
case DeleteRule.NO_ACTION:
break;
case DeleteRule.NULLIFY:
ArcProperty reverseArc = property.getComplimentaryReverseArc();
if (reverseArc == null) {
// nothing we can do here
break;
}
reverseArc.visit(new PropertyVisitor() {
public boolean visitAttribute(AttributeProperty property) {
return false;
}
public boolean visitToMany(ToManyProperty property) {
for (Persistent relatedObject : relatedObjects) {
property.removeTarget(relatedObject, object, true);
}
return false;
}
public boolean visitToOne(ToOneProperty property) {
// nullify the reverse relationship
for (Persistent relatedObject : relatedObjects) {
property.setTarget(relatedObject, null, true);
}
return false;
}
});
break;
case DeleteRule.CASCADE:
// Delete all related objects
for (Persistent relatedObject : relatedObjects) {
performDelete(relatedObject);
}
break;
default:
object.setPersistenceState(oldState);
throw new CayenneRuntimeException("Invalid delete rule %s", relationship.getDeleteRule());
}
}
}
use of org.apache.cayenne.reflect.PropertyVisitor in project cayenne by apache.
the class DataContext method currentSnapshot.
/**
* Returns a DataRow reflecting current, possibly uncommitted, object state.
* <p>
* <strong>Warning:</strong> This method will return a partial snapshot if
* an object or one of its related objects that propagate their keys to this
* object have temporary ids. DO NOT USE this method if you expect a DataRow
* to represent a complete object state.
* </p>
*
* @since 1.1
*/
public DataRow currentSnapshot(final Persistent object) {
// for a HOLLOW object return snapshot from cache
if (object.getPersistenceState() == PersistenceState.HOLLOW && object.getObjectContext() != null) {
return getObjectStore().getSnapshot(object.getObjectId());
}
ObjEntity entity = getEntityResolver().getObjEntity(object);
final ClassDescriptor descriptor = getEntityResolver().getClassDescriptor(entity.getName());
final DataRow snapshot = new DataRow(10);
snapshot.setEntityName(entity.getName());
descriptor.visitProperties(new PropertyVisitor() {
public boolean visitAttribute(AttributeProperty property) {
ObjAttribute objAttr = property.getAttribute();
// processing compound attributes correctly
snapshot.put(objAttr.getDbAttributePath(), property.readPropertyDirectly(object));
return true;
}
public boolean visitToMany(ToManyProperty property) {
// do nothing
return true;
}
public boolean visitToOne(ToOneProperty property) {
ObjRelationship rel = property.getRelationship();
// if target doesn't propagates its key value, skip it
if (rel.isSourceIndependentFromTargetChange()) {
return true;
}
Object targetObject = property.readPropertyDirectly(object);
if (targetObject == null) {
return true;
}
// to avoid unneeded fault triggering
if (targetObject instanceof Fault) {
DataRow storedSnapshot = getObjectStore().getSnapshot(object.getObjectId());
if (storedSnapshot == null) {
throw new CayenneRuntimeException("No matching objects found for ObjectId %s" + ". Object may have been deleted externally.", object.getObjectId());
}
DbRelationship dbRel = rel.getDbRelationships().get(0);
for (DbJoin join : dbRel.getJoins()) {
String key = join.getSourceName();
snapshot.put(key, storedSnapshot.get(key));
}
return true;
}
// target is resolved and we have an FK->PK to it,
// so extract it from target...
Persistent target = (Persistent) targetObject;
Map<String, Object> idParts = target.getObjectId().getIdSnapshot();
// this method.
if (idParts.isEmpty()) {
return true;
}
DbRelationship dbRel = rel.getDbRelationships().get(0);
Map<String, Object> fk = dbRel.srcFkSnapshotWithTargetSnapshot(idParts);
snapshot.putAll(fk);
return true;
}
});
// process object id map
// we should ignore any object id values if a corresponding attribute
// is a part of relationship "toMasterPK", since those values have been
// set above when db relationships where processed.
Map<String, Object> thisIdParts = object.getObjectId().getIdSnapshot();
if (thisIdParts != null) {
// put only those that do not exist in the map
for (Map.Entry<String, Object> entry : thisIdParts.entrySet()) {
String nextKey = entry.getKey();
if (!snapshot.containsKey(nextKey)) {
snapshot.put(nextKey, entry.getValue());
}
}
}
return snapshot;
}
use of org.apache.cayenne.reflect.PropertyVisitor in project cayenne by apache.
the class DataContextMergeHandler method arcChanged.
// works the same for add and remove as long as we don't get too smart per TODO below.
private void arcChanged(Object nodeId, Object targetNodeId, Object arcId) {
final Persistent source = (Persistent) context.getGraphManager().getNode(nodeId);
if (source != null && source.getPersistenceState() != PersistenceState.HOLLOW) {
final int state = source.getPersistenceState();
PropertyDescriptor p = propertyForId(nodeId, arcId.toString());
p.visit(new PropertyVisitor() {
public boolean visitAttribute(AttributeProperty property) {
return false;
}
public boolean visitToMany(ToManyProperty property) {
if (state == PersistenceState.COMMITTED) {
property.invalidate(source);
}
return false;
}
public boolean visitToOne(ToOneProperty property) {
if (state == PersistenceState.COMMITTED) {
property.invalidate(source);
}
// of dirty objects. See DataRowUtils for details.
return false;
}
});
}
}
use of org.apache.cayenne.reflect.PropertyVisitor in project cayenne by apache.
the class DataRowUtils method forceMergeWithSnapshot.
static void forceMergeWithSnapshot(final DataContext context, ClassDescriptor descriptor, final Persistent object, final DataRow snapshot) {
final ObjectDiff diff = context.getObjectStore().getChangesByObjectId().get(object.getObjectId());
descriptor.visitProperties(new PropertyVisitor() {
public boolean visitAttribute(AttributeProperty property) {
String dbAttrPath = property.getAttribute().getDbAttributePath();
// supports merging of partial snapshots...
// check for null is cheaper than double lookup
// for a key... so check for partial snapshot
// only if the value is null
Object newValue = snapshot.get(dbAttrPath);
if (newValue != null || snapshot.containsKey(dbAttrPath)) {
Object curValue = property.readPropertyDirectly(object);
Object oldValue = diff != null ? diff.getSnapshotValue(property.getName()) : null;
// otherwise leave it alone
if (Util.nullSafeEquals(curValue, oldValue) && !Util.nullSafeEquals(newValue, curValue)) {
property.writePropertyDirectly(object, oldValue, newValue);
}
}
return true;
}
public boolean visitToMany(ToManyProperty property) {
// noop - nothing to merge
return true;
}
public boolean visitToOne(ToOneProperty property) {
ObjRelationship relationship = property.getRelationship();
if (relationship.isToPK()) {
// otherwise leave it alone
if (!isToOneTargetModified(property, object, diff)) {
DbRelationship dbRelationship = relationship.getDbRelationships().get(0);
// snapshots
if (hasFK(dbRelationship, snapshot)) {
ObjectId id = snapshot.createTargetObjectId(relationship.getTargetEntityName(), dbRelationship);
if (diff == null || !diff.containsArcSnapshot(relationship.getName()) || !Util.nullSafeEquals(id, diff.getArcSnapshotValue(relationship.getName()))) {
if (id == null) {
property.writeProperty(object, null, null);
} else {
// .. must turn to fault instead
if (!relationship.isSourceDefiningTargetPrecenseAndType(context.getEntityResolver())) {
property.invalidate(object);
} else {
property.writeProperty(object, null, context.findOrCreateObject(id));
}
}
}
}
}
}
return true;
}
});
}
use of org.apache.cayenne.reflect.PropertyVisitor in project cayenne by apache.
the class ObjectDiff method isNoop.
/**
* Checks whether at least a single property is modified.
*/
@Override
public boolean isNoop() {
// if we have no baseline to compare with, assume that there are changes
if (snapshot == null) {
return false;
}
if (flatIds != null && !flatIds.isEmpty()) {
return false;
}
if (phantomFks != null && !phantomFks.isEmpty()) {
return false;
}
int state = object.getPersistenceState();
if (state == PersistenceState.NEW || state == PersistenceState.DELETED) {
return false;
}
// check phantom mods
final boolean[] modFound = new boolean[1];
getClassDescriptor().visitProperties(new PropertyVisitor() {
@Override
public boolean visitAttribute(AttributeProperty property) {
Object oldValue = snapshot.get(property.getName());
Object newValue = property.readProperty(object);
if (!Util.nullSafeEquals(oldValue, newValue)) {
modFound[0] = true;
}
return !modFound[0];
}
@Override
public boolean visitToMany(ToManyProperty property) {
// flattened changes
return true;
}
@Override
public boolean visitToOne(ToOneProperty property) {
if (arcSnapshot == null) {
return true;
}
Object newValue = property.readPropertyDirectly(object);
if (newValue instanceof Fault) {
return true;
}
Object oldValue = arcSnapshot.get(property.getName());
if (!Util.nullSafeEquals(oldValue, newValue != null ? ((Persistent) newValue).getObjectId() : null)) {
modFound[0] = true;
}
return !modFound[0];
}
});
return !modFound[0];
}
Aggregations