Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/CommitIntegrityCheck.java')
-rw-r--r--plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/CommitIntegrityCheck.java1046
1 files changed, 523 insertions, 523 deletions
diff --git a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/CommitIntegrityCheck.java b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/CommitIntegrityCheck.java
index 0802b4b8be..93e02a670f 100644
--- a/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/CommitIntegrityCheck.java
+++ b/plugins/org.eclipse.emf.cdo/src/org/eclipse/emf/internal/cdo/util/CommitIntegrityCheck.java
@@ -1,523 +1,523 @@
-/*
- * Copyright (c) 2004 - 2012 Eike Stepper (Berlin, Germany) and others.
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * http://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors:
- * Caspar De Groot - initial API and implementation
- */
-package org.eclipse.emf.internal.cdo.util;
-
-import org.eclipse.emf.cdo.CDOObject;
-import org.eclipse.emf.cdo.CDOState;
-import org.eclipse.emf.cdo.common.id.CDOID;
-import org.eclipse.emf.cdo.common.id.CDOIDUtil;
-import org.eclipse.emf.cdo.common.model.EMFUtil;
-import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
-import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
-import org.eclipse.emf.cdo.eresource.CDOResource;
-import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
-import org.eclipse.emf.cdo.util.CDOUtil;
-import org.eclipse.emf.cdo.util.CommitIntegrityException;
-
-import org.eclipse.net4j.util.CheckUtil;
-
-import org.eclipse.emf.common.util.EList;
-import org.eclipse.emf.ecore.EObject;
-import org.eclipse.emf.ecore.EReference;
-import org.eclipse.emf.ecore.EStructuralFeature;
-import org.eclipse.emf.ecore.InternalEObject.EStore;
-import org.eclipse.emf.spi.cdo.InternalCDOObject;
-import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
-import org.eclipse.emf.spi.cdo.InternalCDOTransaction.InternalCDOCommitContext;
-
-import java.util.HashSet;
-import java.util.Set;
-
-/**
- * @author Caspar De Groot
- * @since 4.0
- */
-public class CommitIntegrityCheck
-{
- private InternalCDOTransaction transaction;
-
- private Style style;
-
- private Set<CDOID> newIDs, dirtyIDs, detachedIDs;
-
- private Set<CDOObject> missingObjects = new HashSet<CDOObject>();
-
- private StringBuilder exceptionMessage = new StringBuilder();
-
- public CommitIntegrityCheck(InternalCDOCommitContext commitContext)
- {
- this(commitContext, Style.EXCEPTION_FAST);
- }
-
- public CommitIntegrityCheck(InternalCDOCommitContext commitContext, Style style)
- {
- transaction = commitContext.getTransaction();
-
- CheckUtil.checkArg(style, "style");
- this.style = style;
-
- newIDs = commitContext.getNewObjects().keySet();
- dirtyIDs = commitContext.getDirtyObjects().keySet();
- detachedIDs = commitContext.getDetachedObjects().keySet();
- }
-
- public void check() throws CommitIntegrityException
- {
- // For new objects: ensure that their container is included,
- // as well as the targets of the new object's bidi references
- for (CDOID newID : newIDs)
- {
- CDOObject newObject = transaction.getObject(newID);
- checkContainerIncluded(newObject, "new");
- checkCurrentRefTargetsIncluded(newObject, "new");
- }
-
- // For detached objects: ensure that their former container is included,
- // as well as the targets of the detached object's bidi references
- for (CDOID detachedID : detachedIDs)
- {
- CDOObject detachedObject = transaction.getObject(detachedID);
- checkFormerContainerIncluded(detachedObject);
- checkFormerBidiRefTargetsIncluded(detachedObject, "detached");
- }
-
- // For dirty objects: if any of the deltas for the object, affect containment (i.e. object was moved)
- // or a bi-di reference, ensure that for containment, both the old and new containers are included,
- // (or that the child is included if we are considering the dirty parent),
- // and that for a bi-di reference, the object holding the other end of the bi-di is included,
- // as well as possibly the *former* object holding the other end.
- for (CDOID dirtyID : dirtyIDs)
- {
- CDOObject dirtyObject = transaction.getObject(dirtyID);
- analyzeRevisionDelta((InternalCDOObject)dirtyObject);
- }
-
- if (!missingObjects.isEmpty() && style == Style.EXCEPTION)
- {
- throw createException();
- }
- }
-
- public Set<? extends EObject> getMissingObjects()
- {
- return missingObjects;
- }
-
- private CDOID getContainerOrResourceID(InternalCDORevision revision)
- {
- CDOID containerOrResourceID = null;
- Object idOrObject = revision.getContainerID();
- if (idOrObject != null)
- {
- containerOrResourceID = (CDOID)transaction.convertObjectToID(idOrObject);
- }
-
- if (CDOIDUtil.isNull(containerOrResourceID))
- {
- idOrObject = revision.getResourceID();
- if (idOrObject != null)
- {
- containerOrResourceID = (CDOID)transaction.convertObjectToID(idOrObject);
- }
- }
-
- return containerOrResourceID;
- }
-
- private void analyzeRevisionDelta(InternalCDOObject dirtyObject) throws CommitIntegrityException
- {
- // Getting the deltas from the TX is not a good idea...
- // We better recompute a fresh delta:
- InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
- CheckUtil.checkNull(cleanRev, "Could not obtain clean revision for dirty object " + dirtyObject);
-
- InternalCDORevision dirtyRev = dirtyObject.cdoRevision();
- CDORevisionDelta rDelta = dirtyRev.compare(cleanRev);
-
- for (CDOFeatureDelta featureDelta : rDelta.getFeatureDeltas())
- {
- EStructuralFeature feat = featureDelta.getFeature();
- if (feat == CDOContainerFeatureDelta.CONTAINER_FEATURE)
- {
- // Three possibilities here:
- // 1. Object's container has changed
- // 2. Object's containment feature has changed
- // 3. Object's resource has changed
- // (or several of the above)
-
- // @1
- CDOID currentContainerID = (CDOID)transaction.convertObjectToID(dirtyRev.getContainerID());
- CDOID cleanContainerID = (CDOID)transaction.convertObjectToID(cleanRev.getContainerID());
- if (!CDOIDUtil.equals(currentContainerID, cleanContainerID))
- {
- if (currentContainerID != CDOID.NULL)
- {
- checkIncluded(currentContainerID, "container of moved", dirtyObject);
- }
-
- if (cleanContainerID != CDOID.NULL)
- {
- checkIncluded(cleanContainerID, "former container of moved", dirtyObject);
- }
- }
-
- // @2
- // Nothing to be done. (I think...)
-
- // @3
- CDOID currentResourceID = (CDOID)transaction.convertObjectToID(dirtyRev.getResourceID());
- CDOID cleanResourceID = (CDOID)transaction.convertObjectToID(cleanRev.getResourceID());
- if (!CDOIDUtil.equals(currentResourceID, cleanResourceID))
- {
- if (currentResourceID != CDOID.NULL)
- {
- checkIncluded(currentResourceID, "resource of moved", dirtyObject);
- }
-
- if (cleanResourceID != CDOID.NULL)
- {
- checkIncluded(cleanResourceID, "former resource of moved", dirtyObject);
- }
- }
- }
- else if (feat instanceof EReference)
- {
- if (featureDelta instanceof CDOListFeatureDelta)
- {
- for (CDOFeatureDelta innerFeatDelta : ((CDOListFeatureDelta)featureDelta).getListChanges())
- {
- checkFeatureDelta(innerFeatDelta, dirtyObject);
- }
- }
- else
- {
- checkFeatureDelta(featureDelta, dirtyObject);
- }
- }
- }
- }
-
- private void checkIncluded(Object idOrObject, String msg, CDOObject o) throws CommitIntegrityException
- {
- idOrObject = transaction.convertObjectToID(idOrObject);
- if (idOrObject instanceof CDOID)
- {
- CDOID id = (CDOID)idOrObject;
- if (!id.isNull())
- {
- checkIncluded(id, msg, o);
- }
- }
-
- // else: Transient object -- ignore
- }
-
- private void checkFeatureDelta(CDOFeatureDelta featureDelta, CDOObject dirtyObject) throws CommitIntegrityException
- {
- EReference ref = (EReference)featureDelta.getFeature();
- boolean containmentOrWithOpposite = ref.isContainment() || hasPersistentOpposite(ref);
-
- if (featureDelta instanceof CDOAddFeatureDelta)
- {
- Object idOrObject = ((CDOAddFeatureDelta)featureDelta).getValue();
- if (containmentOrWithOpposite || isNew(idOrObject))
- {
- checkIncluded(idOrObject, "added child / refTarget of", dirtyObject);
- }
- }
- else if (featureDelta instanceof CDOSetFeatureDelta)
- {
- Object oldIDOrObject = ((CDOSetFeatureDelta)featureDelta).getOldValue();
- CDOID oldID = (CDOID)transaction.convertObjectToID(oldIDOrObject);
- if (!CDOIDUtil.isNull(oldID))
- {
- // Old child must be included if it's the container or has an eOpposite
- if (containmentOrWithOpposite)
- {
- checkIncluded(oldID, "removed / former child / refTarget of", dirtyObject);
- }
- }
-
- Object newIDOrObject = ((CDOSetFeatureDelta)featureDelta).getValue();
- if (newIDOrObject != null)
- {
- // New child must be included
- newIDOrObject = transaction.convertObjectToID(newIDOrObject);
- if (containmentOrWithOpposite || isNew(newIDOrObject))
- {
- checkIncluded(newIDOrObject, "new child / refTarget of", dirtyObject);
- }
- }
- }
- else if (containmentOrWithOpposite)
- {
- if (featureDelta instanceof CDORemoveFeatureDelta)
- {
- Object idOrObject = ((CDORemoveFeatureDelta)featureDelta).getValue();
- CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
- checkIncluded(id, "removed child / refTarget of", dirtyObject);
- }
- else if (featureDelta instanceof CDOClearFeatureDelta)
- {
- EStructuralFeature feat = ((CDOClearFeatureDelta)featureDelta).getFeature();
- InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
- int n = cleanRev.size(feat);
- for (int i = 0; i < n; i++)
- {
- Object idOrObject = cleanRev.get(feat, i);
- CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
- checkIncluded(id, "removed child / refTarget of", dirtyObject);
- }
- }
- else if (featureDelta instanceof CDOUnsetFeatureDelta)
- {
- EStructuralFeature feat = ((CDOUnsetFeatureDelta)featureDelta).getFeature();
- InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
- Object idOrObject = cleanRev.getValue(feat);
- CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
- checkIncluded(id, "removed child / refTarget of", dirtyObject);
- }
- else if (featureDelta instanceof CDOMoveFeatureDelta)
- {
- // Nothing to do: a move doesn't affect the child being moved
- // so that child does not need to be included
- }
- else
- {
- throw new IllegalArgumentException("Unexpected delta type: " + featureDelta.getClass().getSimpleName());
- }
- }
- }
-
- private boolean isNew(Object idOrObject)
- {
- CDOObject object = null;
- if (idOrObject instanceof CDOObject)
- {
- object = (CDOObject)idOrObject;
- }
- else if (idOrObject instanceof EObject)
- {
- object = CDOUtil.getCDOObject((EObject)idOrObject);
- }
- else if (idOrObject instanceof CDOID)
- {
- object = transaction.getObject((CDOID)idOrObject);
- }
-
- if (object != null)
- {
- return object.cdoState() == CDOState.NEW;
- }
-
- return false;
- }
-
- private void checkIncluded(CDOID id, String msg, CDOObject o) throws CommitIntegrityException
- {
- if (id.isNull())
- {
- throw new IllegalArgumentException("CDOID must not be NULL");
- }
-
- if (!dirtyIDs.contains(id) && !detachedIDs.contains(id) && !newIDs.contains(id))
- {
- CDOObject missingObject = transaction.getObject(id);
- if (missingObject == null)
- {
- throw new IllegalStateException("Could not find object for CDOID " + id);
- }
-
- missingObjects.add(missingObject);
-
- if (exceptionMessage.length() > 0)
- {
- exceptionMessage.append('\n');
- }
-
- String m = String.format("The %s object %s needs to be included in the commit but isn't", msg, o);
- exceptionMessage.append(m);
-
- if (style == Style.EXCEPTION_FAST)
- {
- throw createException();
- }
- }
- }
-
- private CommitIntegrityException createException()
- {
- return new CommitIntegrityException(exceptionMessage.toString(), missingObjects);
- }
-
- /**
- * Checks whether the container of a given object is included in the commit
- */
- private void checkContainerIncluded(CDOObject object, String msgFrag) throws CommitIntegrityException
- {
- EObject eContainer = object.eContainer();
- if (eContainer == null)
- {
- // It's a top-level object
- CDOResource resource = object.cdoDirectResource();
- checkIncluded(resource.cdoID(), "resource of " + msgFrag, object);
- }
- else
- {
- CDOObject container = CDOUtil.getCDOObject(eContainer);
- checkIncluded(container.cdoID(), "container of " + msgFrag, object);
- }
- }
-
- private void checkCurrentRefTargetsIncluded(CDOObject referencer, String msgFrag) throws CommitIntegrityException
- {
- for (EReference eRef : referencer.eClass().getEAllReferences())
- {
- if (EMFUtil.isPersistent(eRef))
- {
- if (eRef.isMany())
- {
- EList<?> list = (EList<?>)referencer.eGet(eRef);
- for (Object refTarget : list)
- {
- checkBidiRefTargetOrNewNonBidiTargetIncluded(referencer, eRef, refTarget, msgFrag);
- }
- }
- else
- {
- Object refTarget = referencer.eGet(eRef);
- if (refTarget != null)
- {
- checkBidiRefTargetOrNewNonBidiTargetIncluded(referencer, eRef, refTarget, msgFrag);
- }
- }
- }
- }
- }
-
- private void checkBidiRefTargetOrNewNonBidiTargetIncluded(CDOObject referencer, EReference eRef, Object refTarget,
- String msgFrag) throws CommitIntegrityException
- {
- if (hasPersistentOpposite(eRef))
- {
- // It's a bi-di ref; the target must definitely be included
- checkBidiRefTargetIncluded(refTarget, referencer, eRef.getName(), msgFrag);
- }
- else if (isNew(refTarget))
- {
- // It's a non-bidi ref; the target doesn't have to be included unless it's NEW
- checkIncluded(refTarget, "target of reference '" + eRef.getName() + "' of " + msgFrag, referencer);
- }
- }
-
- private void checkFormerBidiRefTargetsIncluded(CDOObject referencer, String msgFrag) throws CommitIntegrityException
- {
- // The referencer argument should really be a detached object, and so we know
- // that we can find the pre-detach revision in tx.getFormerRevisions(). However,
- // the object may have already been dirty prior to detachment, so we check the
- // clean revisions first.
- InternalCDORevision cleanRev = transaction.getCleanRevisions().get(referencer);
- CheckUtil.checkState(cleanRev, "cleanRev");
-
- for (EReference eRef : referencer.eClass().getEAllReferences())
- {
- if (EMFUtil.isPersistent(eRef) && hasPersistentOpposite(eRef))
- {
- Object value = cleanRev.get(eRef, EStore.NO_INDEX);
- if (value != null)
- {
- if (eRef.isMany())
- {
- EList<?> list = (EList<?>)value;
- for (Object element : list)
- {
- checkBidiRefTargetIncluded(element, referencer, eRef.getName(), msgFrag);
- }
- }
- else
- {
- checkBidiRefTargetIncluded(value, referencer, eRef.getName(), msgFrag);
- }
- }
- }
- }
- }
-
- private void checkBidiRefTargetIncluded(Object refTarget, CDOObject referencer, String refName, String msgFrag)
- throws CommitIntegrityException
- {
- CheckUtil.checkArg(refTarget, "refTarget");
- CDOID refTargetID = null;
- if (refTarget instanceof EObject)
- {
- refTargetID = CDOUtil.getCDOObject((EObject)refTarget).cdoID();
- if (refTargetID == null)
- {
- // No ID, means object is TRANSIENT; ignore.
- return;
- }
- }
- else if (refTarget instanceof CDOID)
- {
- refTargetID = (CDOID)refTarget;
- }
-
- checkIncluded(refTargetID, "target of reference '" + refName + "' of " + msgFrag, referencer);
- }
-
- private void checkFormerContainerIncluded(CDOObject detachedObject) throws CommitIntegrityException
- {
- InternalCDORevision rev = transaction.getCleanRevisions().get(detachedObject);
- CheckUtil.checkNull(rev, "Could not obtain clean revision for detached object " + detachedObject);
-
- CDOID id = getContainerOrResourceID(rev);
- checkIncluded(id, "former container (or resource) of detached", detachedObject);
- }
-
- private static boolean hasPersistentOpposite(EReference ref)
- {
- EReference eOpposite = ref.getEOpposite();
- return eOpposite != null && EMFUtil.isPersistent(eOpposite);
- }
-
- /**
- * Designates an exception style for a {@link CommitIntegrityCheck}
- *
- * @author Caspar De Groot
- */
- public static enum Style
- {
- /**
- * Throw an exception as soon as this {@link CommitIntegrityCheck} encounters the first problem
- */
- EXCEPTION_FAST,
-
- /**
- * Throw an exception when this {@link CommitIntegrityCheck} finishes performing all possible checks, in case any
- * problems were found
- */
- EXCEPTION,
-
- /**
- * Do not throw an exception. Caller must invoke {@link CommitIntegrityCheck#getMissingObjects()} to find out if the
- * check discovered any problems.
- */
- NO_EXCEPTION
- }
-}
+/*
+ * Copyright (c) 2004 - 2012 Eike Stepper (Berlin, Germany) and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * Caspar De Groot - initial API and implementation
+ */
+package org.eclipse.emf.internal.cdo.util;
+
+import org.eclipse.emf.cdo.CDOObject;
+import org.eclipse.emf.cdo.CDOState;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.id.CDOIDUtil;
+import org.eclipse.emf.cdo.common.model.EMFUtil;
+import org.eclipse.emf.cdo.common.revision.delta.CDOAddFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOClearFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOContainerFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOListFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOMoveFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDORemoveFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDORevisionDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOSetFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
+import org.eclipse.emf.cdo.eresource.CDOResource;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+import org.eclipse.emf.cdo.util.CDOUtil;
+import org.eclipse.emf.cdo.util.CommitIntegrityException;
+
+import org.eclipse.net4j.util.CheckUtil;
+
+import org.eclipse.emf.common.util.EList;
+import org.eclipse.emf.ecore.EObject;
+import org.eclipse.emf.ecore.EReference;
+import org.eclipse.emf.ecore.EStructuralFeature;
+import org.eclipse.emf.ecore.InternalEObject.EStore;
+import org.eclipse.emf.spi.cdo.InternalCDOObject;
+import org.eclipse.emf.spi.cdo.InternalCDOTransaction;
+import org.eclipse.emf.spi.cdo.InternalCDOTransaction.InternalCDOCommitContext;
+
+import java.util.HashSet;
+import java.util.Set;
+
+/**
+ * @author Caspar De Groot
+ * @since 4.0
+ */
+public class CommitIntegrityCheck
+{
+ private InternalCDOTransaction transaction;
+
+ private Style style;
+
+ private Set<CDOID> newIDs, dirtyIDs, detachedIDs;
+
+ private Set<CDOObject> missingObjects = new HashSet<CDOObject>();
+
+ private StringBuilder exceptionMessage = new StringBuilder();
+
+ public CommitIntegrityCheck(InternalCDOCommitContext commitContext)
+ {
+ this(commitContext, Style.EXCEPTION_FAST);
+ }
+
+ public CommitIntegrityCheck(InternalCDOCommitContext commitContext, Style style)
+ {
+ transaction = commitContext.getTransaction();
+
+ CheckUtil.checkArg(style, "style");
+ this.style = style;
+
+ newIDs = commitContext.getNewObjects().keySet();
+ dirtyIDs = commitContext.getDirtyObjects().keySet();
+ detachedIDs = commitContext.getDetachedObjects().keySet();
+ }
+
+ public void check() throws CommitIntegrityException
+ {
+ // For new objects: ensure that their container is included,
+ // as well as the targets of the new object's bidi references
+ for (CDOID newID : newIDs)
+ {
+ CDOObject newObject = transaction.getObject(newID);
+ checkContainerIncluded(newObject, "new");
+ checkCurrentRefTargetsIncluded(newObject, "new");
+ }
+
+ // For detached objects: ensure that their former container is included,
+ // as well as the targets of the detached object's bidi references
+ for (CDOID detachedID : detachedIDs)
+ {
+ CDOObject detachedObject = transaction.getObject(detachedID);
+ checkFormerContainerIncluded(detachedObject);
+ checkFormerBidiRefTargetsIncluded(detachedObject, "detached");
+ }
+
+ // For dirty objects: if any of the deltas for the object, affect containment (i.e. object was moved)
+ // or a bi-di reference, ensure that for containment, both the old and new containers are included,
+ // (or that the child is included if we are considering the dirty parent),
+ // and that for a bi-di reference, the object holding the other end of the bi-di is included,
+ // as well as possibly the *former* object holding the other end.
+ for (CDOID dirtyID : dirtyIDs)
+ {
+ CDOObject dirtyObject = transaction.getObject(dirtyID);
+ analyzeRevisionDelta((InternalCDOObject)dirtyObject);
+ }
+
+ if (!missingObjects.isEmpty() && style == Style.EXCEPTION)
+ {
+ throw createException();
+ }
+ }
+
+ public Set<? extends EObject> getMissingObjects()
+ {
+ return missingObjects;
+ }
+
+ private CDOID getContainerOrResourceID(InternalCDORevision revision)
+ {
+ CDOID containerOrResourceID = null;
+ Object idOrObject = revision.getContainerID();
+ if (idOrObject != null)
+ {
+ containerOrResourceID = (CDOID)transaction.convertObjectToID(idOrObject);
+ }
+
+ if (CDOIDUtil.isNull(containerOrResourceID))
+ {
+ idOrObject = revision.getResourceID();
+ if (idOrObject != null)
+ {
+ containerOrResourceID = (CDOID)transaction.convertObjectToID(idOrObject);
+ }
+ }
+
+ return containerOrResourceID;
+ }
+
+ private void analyzeRevisionDelta(InternalCDOObject dirtyObject) throws CommitIntegrityException
+ {
+ // Getting the deltas from the TX is not a good idea...
+ // We better recompute a fresh delta:
+ InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
+ CheckUtil.checkNull(cleanRev, "Could not obtain clean revision for dirty object " + dirtyObject);
+
+ InternalCDORevision dirtyRev = dirtyObject.cdoRevision();
+ CDORevisionDelta rDelta = dirtyRev.compare(cleanRev);
+
+ for (CDOFeatureDelta featureDelta : rDelta.getFeatureDeltas())
+ {
+ EStructuralFeature feat = featureDelta.getFeature();
+ if (feat == CDOContainerFeatureDelta.CONTAINER_FEATURE)
+ {
+ // Three possibilities here:
+ // 1. Object's container has changed
+ // 2. Object's containment feature has changed
+ // 3. Object's resource has changed
+ // (or several of the above)
+
+ // @1
+ CDOID currentContainerID = (CDOID)transaction.convertObjectToID(dirtyRev.getContainerID());
+ CDOID cleanContainerID = (CDOID)transaction.convertObjectToID(cleanRev.getContainerID());
+ if (!CDOIDUtil.equals(currentContainerID, cleanContainerID))
+ {
+ if (currentContainerID != CDOID.NULL)
+ {
+ checkIncluded(currentContainerID, "container of moved", dirtyObject);
+ }
+
+ if (cleanContainerID != CDOID.NULL)
+ {
+ checkIncluded(cleanContainerID, "former container of moved", dirtyObject);
+ }
+ }
+
+ // @2
+ // Nothing to be done. (I think...)
+
+ // @3
+ CDOID currentResourceID = (CDOID)transaction.convertObjectToID(dirtyRev.getResourceID());
+ CDOID cleanResourceID = (CDOID)transaction.convertObjectToID(cleanRev.getResourceID());
+ if (!CDOIDUtil.equals(currentResourceID, cleanResourceID))
+ {
+ if (currentResourceID != CDOID.NULL)
+ {
+ checkIncluded(currentResourceID, "resource of moved", dirtyObject);
+ }
+
+ if (cleanResourceID != CDOID.NULL)
+ {
+ checkIncluded(cleanResourceID, "former resource of moved", dirtyObject);
+ }
+ }
+ }
+ else if (feat instanceof EReference)
+ {
+ if (featureDelta instanceof CDOListFeatureDelta)
+ {
+ for (CDOFeatureDelta innerFeatDelta : ((CDOListFeatureDelta)featureDelta).getListChanges())
+ {
+ checkFeatureDelta(innerFeatDelta, dirtyObject);
+ }
+ }
+ else
+ {
+ checkFeatureDelta(featureDelta, dirtyObject);
+ }
+ }
+ }
+ }
+
+ private void checkIncluded(Object idOrObject, String msg, CDOObject o) throws CommitIntegrityException
+ {
+ idOrObject = transaction.convertObjectToID(idOrObject);
+ if (idOrObject instanceof CDOID)
+ {
+ CDOID id = (CDOID)idOrObject;
+ if (!id.isNull())
+ {
+ checkIncluded(id, msg, o);
+ }
+ }
+
+ // else: Transient object -- ignore
+ }
+
+ private void checkFeatureDelta(CDOFeatureDelta featureDelta, CDOObject dirtyObject) throws CommitIntegrityException
+ {
+ EReference ref = (EReference)featureDelta.getFeature();
+ boolean containmentOrWithOpposite = ref.isContainment() || hasPersistentOpposite(ref);
+
+ if (featureDelta instanceof CDOAddFeatureDelta)
+ {
+ Object idOrObject = ((CDOAddFeatureDelta)featureDelta).getValue();
+ if (containmentOrWithOpposite || isNew(idOrObject))
+ {
+ checkIncluded(idOrObject, "added child / refTarget of", dirtyObject);
+ }
+ }
+ else if (featureDelta instanceof CDOSetFeatureDelta)
+ {
+ Object oldIDOrObject = ((CDOSetFeatureDelta)featureDelta).getOldValue();
+ CDOID oldID = (CDOID)transaction.convertObjectToID(oldIDOrObject);
+ if (!CDOIDUtil.isNull(oldID))
+ {
+ // Old child must be included if it's the container or has an eOpposite
+ if (containmentOrWithOpposite)
+ {
+ checkIncluded(oldID, "removed / former child / refTarget of", dirtyObject);
+ }
+ }
+
+ Object newIDOrObject = ((CDOSetFeatureDelta)featureDelta).getValue();
+ if (newIDOrObject != null)
+ {
+ // New child must be included
+ newIDOrObject = transaction.convertObjectToID(newIDOrObject);
+ if (containmentOrWithOpposite || isNew(newIDOrObject))
+ {
+ checkIncluded(newIDOrObject, "new child / refTarget of", dirtyObject);
+ }
+ }
+ }
+ else if (containmentOrWithOpposite)
+ {
+ if (featureDelta instanceof CDORemoveFeatureDelta)
+ {
+ Object idOrObject = ((CDORemoveFeatureDelta)featureDelta).getValue();
+ CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
+ checkIncluded(id, "removed child / refTarget of", dirtyObject);
+ }
+ else if (featureDelta instanceof CDOClearFeatureDelta)
+ {
+ EStructuralFeature feat = ((CDOClearFeatureDelta)featureDelta).getFeature();
+ InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
+ int n = cleanRev.size(feat);
+ for (int i = 0; i < n; i++)
+ {
+ Object idOrObject = cleanRev.get(feat, i);
+ CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
+ checkIncluded(id, "removed child / refTarget of", dirtyObject);
+ }
+ }
+ else if (featureDelta instanceof CDOUnsetFeatureDelta)
+ {
+ EStructuralFeature feat = ((CDOUnsetFeatureDelta)featureDelta).getFeature();
+ InternalCDORevision cleanRev = transaction.getCleanRevisions().get(dirtyObject);
+ Object idOrObject = cleanRev.getValue(feat);
+ CDOID id = (CDOID)transaction.convertObjectToID(idOrObject);
+ checkIncluded(id, "removed child / refTarget of", dirtyObject);
+ }
+ else if (featureDelta instanceof CDOMoveFeatureDelta)
+ {
+ // Nothing to do: a move doesn't affect the child being moved
+ // so that child does not need to be included
+ }
+ else
+ {
+ throw new IllegalArgumentException("Unexpected delta type: " + featureDelta.getClass().getSimpleName());
+ }
+ }
+ }
+
+ private boolean isNew(Object idOrObject)
+ {
+ CDOObject object = null;
+ if (idOrObject instanceof CDOObject)
+ {
+ object = (CDOObject)idOrObject;
+ }
+ else if (idOrObject instanceof EObject)
+ {
+ object = CDOUtil.getCDOObject((EObject)idOrObject);
+ }
+ else if (idOrObject instanceof CDOID)
+ {
+ object = transaction.getObject((CDOID)idOrObject);
+ }
+
+ if (object != null)
+ {
+ return object.cdoState() == CDOState.NEW;
+ }
+
+ return false;
+ }
+
+ private void checkIncluded(CDOID id, String msg, CDOObject o) throws CommitIntegrityException
+ {
+ if (id.isNull())
+ {
+ throw new IllegalArgumentException("CDOID must not be NULL");
+ }
+
+ if (!dirtyIDs.contains(id) && !detachedIDs.contains(id) && !newIDs.contains(id))
+ {
+ CDOObject missingObject = transaction.getObject(id);
+ if (missingObject == null)
+ {
+ throw new IllegalStateException("Could not find object for CDOID " + id);
+ }
+
+ missingObjects.add(missingObject);
+
+ if (exceptionMessage.length() > 0)
+ {
+ exceptionMessage.append('\n');
+ }
+
+ String m = String.format("The %s object %s needs to be included in the commit but isn't", msg, o);
+ exceptionMessage.append(m);
+
+ if (style == Style.EXCEPTION_FAST)
+ {
+ throw createException();
+ }
+ }
+ }
+
+ private CommitIntegrityException createException()
+ {
+ return new CommitIntegrityException(exceptionMessage.toString(), missingObjects);
+ }
+
+ /**
+ * Checks whether the container of a given object is included in the commit
+ */
+ private void checkContainerIncluded(CDOObject object, String msgFrag) throws CommitIntegrityException
+ {
+ EObject eContainer = object.eContainer();
+ if (eContainer == null)
+ {
+ // It's a top-level object
+ CDOResource resource = object.cdoDirectResource();
+ checkIncluded(resource.cdoID(), "resource of " + msgFrag, object);
+ }
+ else
+ {
+ CDOObject container = CDOUtil.getCDOObject(eContainer);
+ checkIncluded(container.cdoID(), "container of " + msgFrag, object);
+ }
+ }
+
+ private void checkCurrentRefTargetsIncluded(CDOObject referencer, String msgFrag) throws CommitIntegrityException
+ {
+ for (EReference eRef : referencer.eClass().getEAllReferences())
+ {
+ if (EMFUtil.isPersistent(eRef))
+ {
+ if (eRef.isMany())
+ {
+ EList<?> list = (EList<?>)referencer.eGet(eRef);
+ for (Object refTarget : list)
+ {
+ checkBidiRefTargetOrNewNonBidiTargetIncluded(referencer, eRef, refTarget, msgFrag);
+ }
+ }
+ else
+ {
+ Object refTarget = referencer.eGet(eRef);
+ if (refTarget != null)
+ {
+ checkBidiRefTargetOrNewNonBidiTargetIncluded(referencer, eRef, refTarget, msgFrag);
+ }
+ }
+ }
+ }
+ }
+
+ private void checkBidiRefTargetOrNewNonBidiTargetIncluded(CDOObject referencer, EReference eRef, Object refTarget,
+ String msgFrag) throws CommitIntegrityException
+ {
+ if (hasPersistentOpposite(eRef))
+ {
+ // It's a bi-di ref; the target must definitely be included
+ checkBidiRefTargetIncluded(refTarget, referencer, eRef.getName(), msgFrag);
+ }
+ else if (isNew(refTarget))
+ {
+ // It's a non-bidi ref; the target doesn't have to be included unless it's NEW
+ checkIncluded(refTarget, "target of reference '" + eRef.getName() + "' of " + msgFrag, referencer);
+ }
+ }
+
+ private void checkFormerBidiRefTargetsIncluded(CDOObject referencer, String msgFrag) throws CommitIntegrityException
+ {
+ // The referencer argument should really be a detached object, and so we know
+ // that we can find the pre-detach revision in tx.getFormerRevisions(). However,
+ // the object may have already been dirty prior to detachment, so we check the
+ // clean revisions first.
+ InternalCDORevision cleanRev = transaction.getCleanRevisions().get(referencer);
+ CheckUtil.checkState(cleanRev, "cleanRev");
+
+ for (EReference eRef : referencer.eClass().getEAllReferences())
+ {
+ if (EMFUtil.isPersistent(eRef) && hasPersistentOpposite(eRef))
+ {
+ Object value = cleanRev.get(eRef, EStore.NO_INDEX);
+ if (value != null)
+ {
+ if (eRef.isMany())
+ {
+ EList<?> list = (EList<?>)value;
+ for (Object element : list)
+ {
+ checkBidiRefTargetIncluded(element, referencer, eRef.getName(), msgFrag);
+ }
+ }
+ else
+ {
+ checkBidiRefTargetIncluded(value, referencer, eRef.getName(), msgFrag);
+ }
+ }
+ }
+ }
+ }
+
+ private void checkBidiRefTargetIncluded(Object refTarget, CDOObject referencer, String refName, String msgFrag)
+ throws CommitIntegrityException
+ {
+ CheckUtil.checkArg(refTarget, "refTarget");
+ CDOID refTargetID = null;
+ if (refTarget instanceof EObject)
+ {
+ refTargetID = CDOUtil.getCDOObject((EObject)refTarget).cdoID();
+ if (refTargetID == null)
+ {
+ // No ID, means object is TRANSIENT; ignore.
+ return;
+ }
+ }
+ else if (refTarget instanceof CDOID)
+ {
+ refTargetID = (CDOID)refTarget;
+ }
+
+ checkIncluded(refTargetID, "target of reference '" + refName + "' of " + msgFrag, referencer);
+ }
+
+ private void checkFormerContainerIncluded(CDOObject detachedObject) throws CommitIntegrityException
+ {
+ InternalCDORevision rev = transaction.getCleanRevisions().get(detachedObject);
+ CheckUtil.checkNull(rev, "Could not obtain clean revision for detached object " + detachedObject);
+
+ CDOID id = getContainerOrResourceID(rev);
+ checkIncluded(id, "former container (or resource) of detached", detachedObject);
+ }
+
+ private static boolean hasPersistentOpposite(EReference ref)
+ {
+ EReference eOpposite = ref.getEOpposite();
+ return eOpposite != null && EMFUtil.isPersistent(eOpposite);
+ }
+
+ /**
+ * Designates an exception style for a {@link CommitIntegrityCheck}
+ *
+ * @author Caspar De Groot
+ */
+ public static enum Style
+ {
+ /**
+ * Throw an exception as soon as this {@link CommitIntegrityCheck} encounters the first problem
+ */
+ EXCEPTION_FAST,
+
+ /**
+ * Throw an exception when this {@link CommitIntegrityCheck} finishes performing all possible checks, in case any
+ * problems were found
+ */
+ EXCEPTION,
+
+ /**
+ * Do not throw an exception. Caller must invoke {@link CommitIntegrityCheck#getMissingObjects()} to find out if the
+ * check discovered any problems.
+ */
+ NO_EXCEPTION
+ }
+}

Back to the top