Skip to main content
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java')
-rw-r--r--org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java825
1 files changed, 825 insertions, 0 deletions
diff --git a/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java
new file mode 100644
index 0000000000..92d96aad20
--- /dev/null
+++ b/org.eclipse.emf.cdo.server.db/src/org/eclipse/emf/cdo/server/internal/db/mapping/horizontal/NonAuditListTableMapping.java
@@ -0,0 +1,825 @@
+/**
+ * Copyright (c) 2004 - 2011 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:
+ * Eike Stepper - initial API and implementation
+ * Stefan Winkler - 271444: [DB] Multiple refactorings bug 271444
+ * Stefan Winkler - Bug 329025: [DB] Support branching for range-based mapping strategy
+ */
+package org.eclipse.emf.cdo.server.internal.db.mapping.horizontal;
+
+import org.eclipse.emf.cdo.common.branch.CDOBranchPoint;
+import org.eclipse.emf.cdo.common.id.CDOID;
+import org.eclipse.emf.cdo.common.revision.CDORevision;
+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.CDOFeatureDeltaVisitor;
+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.CDOSetFeatureDelta;
+import org.eclipse.emf.cdo.common.revision.delta.CDOUnsetFeatureDelta;
+import org.eclipse.emf.cdo.server.db.IDBStoreAccessor;
+import org.eclipse.emf.cdo.server.db.IIDHandler;
+import org.eclipse.emf.cdo.server.db.IPreparedStatementCache.ReuseProbability;
+import org.eclipse.emf.cdo.server.db.mapping.IListMappingDeltaSupport;
+import org.eclipse.emf.cdo.server.db.mapping.IMappingStrategy;
+import org.eclipse.emf.cdo.server.internal.db.CDODBSchema;
+import org.eclipse.emf.cdo.server.internal.db.bundle.OM;
+import org.eclipse.emf.cdo.spi.common.revision.InternalCDORevision;
+
+import org.eclipse.net4j.db.DBException;
+import org.eclipse.net4j.db.DBType;
+import org.eclipse.net4j.db.DBUtil;
+import org.eclipse.net4j.util.om.trace.ContextTracer;
+
+import org.eclipse.emf.ecore.EClass;
+import org.eclipse.emf.ecore.EStructuralFeature;
+
+import org.eclipse.core.runtime.Assert;
+
+import java.sql.PreparedStatement;
+import java.sql.SQLException;
+import java.util.ArrayList;
+
+/**
+ * This is a list-to-table mapping optimized for non-audit-mode. It doesn't care about version and has delta support.
+ *
+ * @author Eike Stepper
+ * @since 2.0
+ */
+public class NonAuditListTableMapping extends AbstractListTableMapping implements IListMappingDeltaSupport
+{
+ private static final ContextTracer TRACER = new ContextTracer(OM.DEBUG, NonAuditListTableMapping.class);
+
+ private FieldInfo[] keyFields;
+
+ private static final int UNBOUNDED_SHIFT = -1;
+
+ /**
+ * The central data structure which is used to calculate the outcomes of the list deltas.
+ */
+ private ArrayList<ManipulationElement> manipulations = new ArrayList<ManipulationElement>();
+
+ /**
+ * This is a flag to remember if a delta of type "clear" has been encountered. If so, the list in the DB has to be
+ * cleared before writing out the changes.
+ */
+ private boolean clearFirst;
+
+ private String sqlClear;
+
+ private String sqlUpdateValue;
+
+ private String sqlUpdateIndex;
+
+ private String sqlInsertValue;
+
+ private String sqlDeleteItem;
+
+ public NonAuditListTableMapping(IMappingStrategy mappingStrategy, EClass eClass, EStructuralFeature feature)
+ {
+ super(mappingStrategy, eClass, feature);
+ initSQLStrings();
+ }
+
+ private void initSQLStrings()
+ {
+ // ----------- clear list -------------------------
+ StringBuilder builder = new StringBuilder();
+
+ builder.append("DELETE FROM "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? "); //$NON-NLS-1$
+
+ sqlClear = builder.toString();
+
+ builder.append(" AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? "); //$NON-NLS-1$
+
+ sqlDeleteItem = builder.toString();
+
+ // ----------- update one item --------------------
+ builder = new StringBuilder();
+ builder.append("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? "); //$NON-NLS-1$
+ sqlUpdateValue = builder.toString();
+
+ // ----------- insert one item --------------------
+ builder = new StringBuilder();
+ builder.append("INSERT INTO "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" ("); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append(", "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_VALUE);
+ builder.append(") VALUES(?, ?, ?) "); //$NON-NLS-1$
+ sqlInsertValue = builder.toString();
+
+ // ----------- update one item index --------------
+ builder = new StringBuilder();
+ builder.append("UPDATE "); //$NON-NLS-1$
+ builder.append(getTable());
+ builder.append(" SET "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? "); //$NON-NLS-1$
+ builder.append(" WHERE "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_REVISION_ID);
+ builder.append("=? AND "); //$NON-NLS-1$
+ builder.append(CDODBSchema.LIST_IDX);
+ builder.append("=? "); //$NON-NLS-1$
+ sqlUpdateIndex = builder.toString();
+ }
+
+ @Override
+ protected FieldInfo[] getKeyFields()
+ {
+ if (keyFields == null)
+ {
+ DBType dbType = getMappingStrategy().getStore().getIDHandler().getDBType();
+ keyFields = new FieldInfo[] { new FieldInfo(CDODBSchema.LIST_REVISION_ID, dbType) };
+ }
+
+ return keyFields;
+ }
+
+ @Override
+ protected void setKeyFields(PreparedStatement stmt, CDORevision revision) throws SQLException
+ {
+ getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, revision.getID());
+ }
+
+ public void objectDetached(IDBStoreAccessor accessor, CDOID id, long revised)
+ {
+ clearList(accessor, id);
+ }
+
+ /**
+ * Clear a list of a given revision.
+ *
+ * @param accessor
+ * the accessor to use
+ * @param id
+ * the id of the revision from which to remove all items
+ */
+ public void clearList(IDBStoreAccessor accessor, CDOID id)
+ {
+ PreparedStatement stmt = null;
+
+ try
+ {
+ stmt = accessor.getStatementCache().getPreparedStatement(sqlClear, ReuseProbability.HIGH);
+ getMappingStrategy().getStore().getIDHandler().setCDOID(stmt, 1, id);
+ DBUtil.update(stmt, false);
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().releasePreparedStatement(stmt);
+ }
+ }
+
+ public void processDelta(final IDBStoreAccessor accessor, final CDOID id, int branchId, int oldVersion,
+ final int newVersion, long created, CDOListFeatureDelta delta)
+ {
+ CDOBranchPoint main = accessor.getStore().getRepository().getBranchManager().getMainBranch().getHead();
+
+ InternalCDORevision originalRevision = (InternalCDORevision)accessor.getStore().getRepository()
+ .getRevisionManager().getRevision(id, main, CDORevision.UNCHUNKED, CDORevision.DEPTH_NONE, true);
+ int oldListSize = originalRevision.getList(getFeature()).size();
+
+ // reset the clear-flag
+ clearFirst = false;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("ListTableMapping.processDelta for revision {0} - previous list size: {1}", originalRevision, //$NON-NLS-1$
+ oldListSize);
+ }
+
+ if (manipulations == null)
+ {
+ manipulations = new ArrayList<ManipulationElement>();
+ }
+ else
+ {
+ manipulations.clear();
+ }
+
+ // create list and initialize with original indexes
+ for (int i = 0; i < oldListSize; i++)
+ {
+ manipulations.add(createOriginalElement(i));
+ }
+
+ // let the visitor collect the changes
+ ListDeltaVisitor visitor = new ListDeltaVisitor();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Procssing deltas..."); //$NON-NLS-1$
+ }
+
+ for (CDOFeatureDelta listDelta : delta.getListChanges())
+ {
+ listDelta.accept(visitor);
+ }
+
+ // TODO: here, we could implement further optimizations.
+ // e.g., if more than 50% of the list's items are removed,
+ // it's better to clear the list and reinsert all values
+ // from scratch.
+
+ // finally, write results to the database
+ writeResultToDatabase(accessor, id);
+ }
+
+ /**
+ * Write calculated changes to the database
+ *
+ * @param accessor
+ * ,
+ */
+ private void writeResultToDatabase(IDBStoreAccessor accessor, CDOID id)
+ {
+ IIDHandler idHandler = getMappingStrategy().getStore().getIDHandler();
+ PreparedStatement deleteStmt = null;
+ PreparedStatement moveStmt = null;
+ PreparedStatement setValueStmt = null;
+ PreparedStatement insertStmt = null;
+
+ int deleteCounter = 0;
+ int moveCounter = 0;
+ int setValueCounter = 0;
+ int insertCounter = 0;
+
+ int tempIndex = -1;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Writing to database:"); //$NON-NLS-1$
+ }
+
+ if (clearFirst)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - clear list"); //$NON-NLS-1$
+ }
+
+ clearList(accessor, id);
+ }
+
+ try
+ {
+ for (ManipulationElement element : manipulations)
+ {
+ if (element.is(ManipulationConstants.DELETE))
+ {
+ /*
+ * Step 1: DELETE all elements e which have e.is(REMOVE) by e.sourceIdx
+ */
+
+ if (deleteStmt == null)
+ {
+ deleteStmt = accessor.getStatementCache().getPreparedStatement(sqlDeleteItem, ReuseProbability.HIGH);
+ idHandler.setCDOID(deleteStmt, 1, id);
+ }
+
+ deleteStmt.setInt(2, element.sourceIndex);
+ deleteStmt.addBatch();
+ deleteCounter++;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - delete at {0} ", element.sourceIndex); //$NON-NLS-1$
+ }
+ }
+
+ if (element.is(ManipulationConstants.MOVE))
+ {
+ /*
+ * Step 2: MOVE all elements e (by e.sourceIdx) which have e.is(MOVE) to temporary idx (-1, -2, -3, -4, ...)
+ * and store temporary idx in e.tempIndex
+ */
+ if (moveStmt == null)
+ {
+ moveStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
+ idHandler.setCDOID(moveStmt, 2, id);
+ }
+
+ moveStmt.setInt(3, element.sourceIndex); // from index
+ moveStmt.setInt(1, --tempIndex); // to index
+ element.tempIndex = tempIndex;
+ moveStmt.addBatch();
+ moveCounter++;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.tempIndex); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /*
+ * Step 3: move all elements which have to be shifted up or down because of add, remove or move of other elements
+ * to their proper position. This has to be done in two phases to avoid collisions, as the index has to be unique
+ */
+ int size = manipulations.size();
+
+ /* Step 3a: shift down */
+ for (int i = 0; i < size; i++)
+ {
+ ManipulationElement element = manipulations.get(i);
+
+ if ((element.type == ManipulationConstants.NONE || element.type == ManipulationConstants.SET_VALUE)
+ && element.sourceIndex > element.destinationIndex)
+ {
+ if (moveStmt == null)
+ {
+ moveStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
+ idHandler.setCDOID(moveStmt, 2, id);
+ }
+
+ moveStmt.setInt(3, element.sourceIndex); // from index
+ moveStmt.setInt(1, element.destinationIndex); // to index
+ moveStmt.addBatch();
+ moveCounter++;
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.destinationIndex); //$NON-NLS-1$
+ }
+ }
+ }
+
+ /* Step 3b: shift up */
+ for (int i = size - 1; i >= 0; i--)
+ {
+ ManipulationElement element = manipulations.get(i);
+
+ if ((element.type == ManipulationConstants.NONE || element.type == ManipulationConstants.SET_VALUE)
+ && element.sourceIndex < element.destinationIndex)
+ {
+ if (moveStmt == null)
+ {
+ moveStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateIndex, ReuseProbability.HIGH);
+ idHandler.setCDOID(moveStmt, 2, id);
+ }
+
+ moveStmt.setInt(3, element.sourceIndex); // from index
+ moveStmt.setInt(1, element.destinationIndex); // to index
+ moveStmt.addBatch();
+ moveCounter++;
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - move {0} -> {1} ", element.sourceIndex, element.destinationIndex); //$NON-NLS-1$
+ }
+ }
+ }
+
+ for (ManipulationElement element : manipulations)
+ {
+ if (element.is(ManipulationConstants.MOVE))
+ {
+ /*
+ * Step 4: MOVE all elements e have e.is(MOVE) from e.tempIdx to e.destinationIdx (because we have moved them
+ * before, moveStmt is always initialized
+ */
+ moveStmt.setInt(3, element.tempIndex); // from index
+ moveStmt.setInt(1, element.destinationIndex); // to index
+ element.tempIndex = tempIndex;
+ moveStmt.addBatch();
+ moveCounter++;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - move {0} -> {1} ", element.tempIndex, element.destinationIndex); //$NON-NLS-1$
+ }
+ }
+
+ if (element.is(ManipulationConstants.SET_VALUE))
+ {
+ /*
+ * Step 5: SET all elements which have e.type == SET_VALUE by index == e.destinationIdx
+ */
+ if (setValueStmt == null)
+ {
+ setValueStmt = accessor.getStatementCache().getPreparedStatement(sqlUpdateValue, ReuseProbability.HIGH);
+ idHandler.setCDOID(setValueStmt, 2, id);
+ }
+
+ setValueStmt.setInt(3, element.destinationIndex);
+ getTypeMapping().setValue(setValueStmt, 1, element.value);
+ setValueStmt.addBatch();
+ setValueCounter++;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - set value at {0} to {1} ", element.destinationIndex, element.value); //$NON-NLS-1$
+ }
+ }
+
+ if (element.is(ManipulationConstants.INSERT))
+ {
+ /*
+ * Step 6: INSERT all elements which have e.type == INSERT.
+ */
+ if (insertStmt == null)
+ {
+ insertStmt = accessor.getStatementCache().getPreparedStatement(sqlInsertValue, ReuseProbability.HIGH);
+ idHandler.setCDOID(insertStmt, 1, id);
+ }
+
+ insertStmt.setInt(2, element.destinationIndex);
+ getTypeMapping().setValue(insertStmt, 3, element.value);
+ insertStmt.addBatch();
+ insertCounter++;
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - insert value at {0} : value {1} ", element.destinationIndex, element.value); //$NON-NLS-1$
+ }
+ }
+ }
+
+ // finally perform all operations
+ if (deleteCounter > 0)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Performing {0} delete operations", deleteCounter); //$NON-NLS-1$
+ }
+
+ DBUtil.executeBatch(deleteStmt, deleteCounter);
+ }
+
+ if (moveCounter > 0)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Performing {0} move operations", moveCounter); //$NON-NLS-1$
+ }
+
+ DBUtil.executeBatch(moveStmt, moveCounter);
+ }
+
+ if (insertCounter > 0)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Performing {0} insert operations", insertCounter); //$NON-NLS-1$
+ }
+
+ DBUtil.executeBatch(insertStmt, insertCounter);
+ }
+
+ if (setValueCounter > 0)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format("Performing {0} set operations", setValueCounter); //$NON-NLS-1$
+ }
+
+ DBUtil.executeBatch(setValueStmt, setValueCounter);
+ }
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ releaseStatement(accessor, deleteStmt, moveStmt, insertStmt, setValueStmt);
+ }
+ }
+
+ private void releaseStatement(IDBStoreAccessor accessor, PreparedStatement... stmts)
+ {
+ Throwable t = null;
+
+ for (PreparedStatement stmt : stmts)
+ {
+ try
+ {
+ if (stmt != null)
+ {
+ try
+ {
+ stmt.clearBatch();
+ }
+ catch (SQLException e)
+ {
+ throw new DBException(e);
+ }
+ finally
+ {
+ accessor.getStatementCache().releasePreparedStatement(stmt);
+ }
+ }
+ }
+ catch (Throwable th)
+ {
+ if (t == null)
+ {
+ // remember first exception
+ t = th;
+ }
+
+ // more exceptions go to the log
+ OM.LOG.error(t);
+ }
+ }
+
+ if (t != null)
+ {
+ throw new DBException(t);
+ }
+ }
+
+ /**
+ * Helper method: shift all (destination) indexes in the interval [from,to] (inclusive at both ends) by offset
+ * (positive or negative).
+ */
+ private void shiftIndexes(int from, int to, int offset)
+ {
+ for (ManipulationElement e : manipulations)
+ {
+ if (e.destinationIndex >= from && (to == UNBOUNDED_SHIFT || e.destinationIndex <= to))
+ {
+ e.destinationIndex += offset;
+ }
+ }
+ }
+
+ /**
+ * Find a manipulation item by destination index).
+ */
+ private ManipulationElement findElement(int index)
+ {
+ for (ManipulationElement e : manipulations)
+ {
+ if (e.destinationIndex == index)
+ {
+ return e;
+ }
+ }
+
+ // never reached
+ Assert.isTrue(false);
+ return null;
+ }
+
+ /**
+ * Delete an element (used in remove and clear)
+ */
+ private void deleteItem(ManipulationElement e)
+ {
+ if (e.is(ManipulationConstants.INSERT))
+ {
+ // newly inserted items are simply removed, as
+ // removing inserted items is equal to no change at all.
+ manipulations.remove(e);
+ }
+ else
+ {
+ // mark the existing item as to be deleted.
+ // (previous MOVE and SET conditions are overridden by setting
+ // the exclusive DELETE type).
+ e.type = ManipulationConstants.DELETE;
+ e.destinationIndex = ManipulationConstants.NO_INDEX;
+ }
+ }
+
+ /**
+ * Create a ManipulationElement which represents an element which already is in the list.
+ */
+ private ManipulationElement createOriginalElement(int index)
+ {
+ return new ManipulationElement(index, index, ManipulationConstants.NIL, ManipulationConstants.NONE);
+ }
+
+ /**
+ * Create a ManipulationElement which represents an element which is inserted in the list.
+ */
+ private ManipulationElement createInsertedElement(int index, Object value)
+ {
+ return new ManipulationElement(ManipulationConstants.NONE, index, value, ManipulationConstants.INSERT);
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private final class ListDeltaVisitor implements CDOFeatureDeltaVisitor
+ {
+ public void visit(CDOAddFeatureDelta delta)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - insert at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$
+ }
+
+ // make room for the new item
+ shiftIndexes(delta.getIndex(), UNBOUNDED_SHIFT, +1);
+
+ // create the item
+ manipulations.add(createInsertedElement(delta.getIndex(), delta.getValue()));
+ }
+
+ public void visit(CDORemoveFeatureDelta delta)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - remove at {0}", delta.getIndex()); //$NON-NLS-1$
+ }
+
+ ManipulationElement e = findElement(delta.getIndex());
+ deleteItem(e);
+
+ // fill the gap by shifting all subsequent items down
+ shiftIndexes(delta.getIndex() + 1, UNBOUNDED_SHIFT, -1);
+ }
+
+ public void visit(CDOSetFeatureDelta delta)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - set at {0} value {1}", delta.getIndex(), delta.getValue()); //$NON-NLS-1$
+ }
+
+ ManipulationElement e = findElement(delta.getIndex());
+ // set the new value
+ e.value = delta.getValue();
+
+ // if the item is freshly inserted we do not set the SET-mark.
+ // setting the value of a new item results in inserting with the
+ // new value at once.
+ if (!e.is(ManipulationConstants.INSERT))
+ {
+ // else mark the existing item to be set to a new value
+ e.addType(ManipulationConstants.SET_VALUE);
+ }
+ }
+
+ public void visit(CDOClearFeatureDelta delta)
+ {
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - clear list"); //$NON-NLS-1$
+ }
+
+ // set the clear-flag
+ clearFirst = true;
+
+ // and also clear all manipulation items
+ manipulations.clear();
+ }
+
+ public void visit(CDOMoveFeatureDelta delta)
+ {
+ int fromIdx = delta.getOldPosition();
+ int toIdx = delta.getNewPosition();
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - move {0} -> {1}", fromIdx, toIdx); //$NON-NLS-1$
+ }
+
+ // ignore the trivial case
+ if (fromIdx == toIdx)
+ {
+ return;
+ }
+
+ ManipulationElement e = findElement(fromIdx);
+
+ // adjust indexes and shift either up or down
+ if (fromIdx < toIdx)
+ {
+ shiftIndexes(fromIdx + 1, toIdx, -1);
+ }
+ else
+ { // fromIdx > toIdx here
+ shiftIndexes(toIdx, fromIdx - 1, +1);
+ }
+
+ // set the new index
+ e.destinationIndex = toIdx;
+
+ // if it is a new element, no MOVE mark needed, because we insert it
+ // at the new position
+ if (!e.is(ManipulationConstants.INSERT))
+ {
+ // else we need to handle the move of an existing item
+ e.addType(ManipulationConstants.MOVE);
+ }
+ }
+
+ public void visit(CDOUnsetFeatureDelta delta)
+ {
+ if (delta.getFeature().isUnsettable())
+ {
+ Assert.isTrue(false);
+ }
+
+ if (TRACER.isEnabled())
+ {
+ TRACER.format(" - unset list"); //$NON-NLS-1$
+ }
+
+ // set the clear-flag
+ clearFirst = true;
+
+ // and also clear all manipulation items
+ manipulations.clear();
+ }
+
+ public void visit(CDOListFeatureDelta delta)
+ {
+ // never called
+ Assert.isTrue(false);
+ }
+
+ public void visit(CDOContainerFeatureDelta delta)
+ {
+ // never called
+ Assert.isTrue(false);
+ }
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class ManipulationConstants
+ {
+ public static final int NO_INDEX = Integer.MIN_VALUE;
+
+ public static final int DELETE = 1 << 4;
+
+ public static final int INSERT = 1 << 3;
+
+ public static final int MOVE = 1 << 2;
+
+ public static final int SET_VALUE = 1 << 1;
+
+ public static final Object NIL = new Object();
+
+ public static final int NONE = 0;
+ }
+
+ /**
+ * @author Eike Stepper
+ */
+ private static final class ManipulationElement
+ {
+ public int type;
+
+ public int sourceIndex;
+
+ public int tempIndex;
+
+ public int destinationIndex;
+
+ public Object value;
+
+ public ManipulationElement(int srcIdx, int dstIdx, Object val, int t)
+ {
+ sourceIndex = srcIdx;
+ tempIndex = ManipulationConstants.NONE;
+ destinationIndex = dstIdx;
+ value = val;
+ type = t;
+ }
+
+ public boolean is(int t)
+ {
+ return (type & t) > 0;
+ }
+
+ public void addType(int t)
+ {
+ type |= t;
+ }
+ }
+}

Back to the top