diff --git a/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/ClientServerOperationPair.java b/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/ClientServerOperationPair.java index 924040a003f..bd42681bc64 100755 --- a/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/ClientServerOperationPair.java +++ b/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/ClientServerOperationPair.java @@ -17,4 +17,14 @@ public class ClientServerOperationPair> { public O getServerOp() { return serverOp; } + + @SuppressWarnings("unchecked") + public static , S> O getNoOp() { + return (O) new Operation() { + @Override + public S applyTo(S toState) { + return toState; // don't change anything, return state unchanged + } + }; + } } diff --git a/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/Operation.java b/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/Operation.java index 0add76223a5..fe204ad8da4 100755 --- a/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/Operation.java +++ b/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/Operation.java @@ -17,4 +17,5 @@ public interface Operation extends Cloneable { * @return the state produced by applying this operation to toState */ S applyTo(S toState); + } diff --git a/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/Transformer.java b/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/Transformer.java index e980fe4af99..bd476488f5f 100755 --- a/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/Transformer.java +++ b/java/com.sap.sailing.operationaltransformation/src/com/sap/sailing/operationaltransformation/Transformer.java @@ -6,7 +6,11 @@ public interface Transformer> { * based on the same state. The complementary pair of operations is computed * such that if the client applies the server operation returned, and the * server applies the client operation returned, both end up in equal states - * again. + * again.

+ * + * If a resulting operation is supposed to not have any effects, return the + * {@link ClientServerOperationPair#getNoOp()} operation, but never null + * as any of the operations in the resulting pair. */ ClientServerOperationPair transform(O clientOp, O serverOp); } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AbstractLeaderboardColumnOperation.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AbstractLeaderboardColumnOperation.java new file mode 100755 index 00000000000..9658b199380 --- /dev/null +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AbstractLeaderboardColumnOperation.java @@ -0,0 +1,25 @@ +package com.sap.sailing.server.operationaltransformation; + + +public abstract class AbstractLeaderboardColumnOperation extends AbstractRacingEventServiceOperation { + private final String leaderboardName; + private final String columnName; + + public AbstractLeaderboardColumnOperation(String leaderboardName, String columnName) { + super(); + this.leaderboardName = leaderboardName; + this.columnName = columnName; + } + + public String getLeaderboardName() { + return leaderboardName; + } + + public String getColumnName() { + return columnName; + } + + protected boolean affectsSameColumn(AbstractLeaderboardColumnOperation other) { + return getLeaderboardName().equals(other.getLeaderboardName()) && getColumnName().equals(other.getColumnName()); + } +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AbstractRacingEventServiceOperation.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AbstractRacingEventServiceOperation.java new file mode 100755 index 00000000000..abafe88a79e --- /dev/null +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AbstractRacingEventServiceOperation.java @@ -0,0 +1,33 @@ +package com.sap.sailing.server.operationaltransformation; + + +/** + * Performs identical transformations for all operation types by simply returning the operation passed. Subclasses need + * to override the transformation methods for those operation types that have an impact for them. + * + * @author Axel Uhl (d043530) + * + */ +public abstract class AbstractRacingEventServiceOperation implements RacingEventServiceOperation { + @Override + public RacingEventServiceOperation transformClientRenameLeaderboardColumnOp( + RenameLeaderboardColumn renameLeaderboardColumnClientOp) { + return renameLeaderboardColumnClientOp; + } + + @Override + public RacingEventServiceOperation transformServerRenameLeaderboardColumnOp( + RenameLeaderboardColumn renameLeaderboardColumnServerOp) { + return renameLeaderboardColumnServerOp; + } + + @Override + public RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard(RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp) { + return removeColumnFromLeaderboardServerOp; + } + + @Override + public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard(RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) { + return removeColumnFromLeaderboardClientOp; + } +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AddColumnToLeaderboard.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AddColumnToLeaderboard.java index 987319c2ccd..b4ca749b45d 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AddColumnToLeaderboard.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/AddColumnToLeaderboard.java @@ -2,27 +2,35 @@ package com.sap.sailing.server.operationaltransformation; import com.sap.sailing.server.RacingEventService; -public class AddColumnToLeaderboard implements RacingEventServiceOperation { - private final String columnName; - private final String leaderboardName; +/** + * Doesn't transform for the {@link RemoveColumnFromLeaderboard} because only existing leaderboards can be removed, + * and adding is only possible for non-existing names. + * + * @author Axel Uhl (d043530) + * + */ +public class AddColumnToLeaderboard extends AbstractLeaderboardColumnOperation { private final boolean medalRace; - public AddColumnToLeaderboard(String columnName, String leaderboardName, boolean medalRace) { - super(); - this.columnName = columnName; - this.leaderboardName = leaderboardName; + super(leaderboardName, columnName); this.medalRace = medalRace; } @Override public RacingEventService applyTo(RacingEventService toState) { - toState.addColumnToLeaderboard(columnName, leaderboardName, medalRace); + toState.addColumnToLeaderboard(getColumnName(), getLeaderboardName(), medalRace); return toState; } @Override - public RacingEventServiceOperation transformFor(RacingEventServiceOperation peerOp) { + public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { + // TODO Auto-generated method stub + return null; + } + + @Override + public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { // TODO Auto-generated method stub return null; } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/MoveLeaderboardColumnDown.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/MoveLeaderboardColumnDown.java index f851596d0e3..00d5f2a1bc3 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/MoveLeaderboardColumnDown.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/MoveLeaderboardColumnDown.java @@ -1,28 +1,55 @@ package com.sap.sailing.server.operationaltransformation; +import com.sap.sailing.operationaltransformation.ClientServerOperationPair; import com.sap.sailing.server.RacingEventService; -public class MoveLeaderboardColumnDown implements RacingEventServiceOperation { - private final String leaderboardName; - private final String columnName; +public class MoveLeaderboardColumnDown extends AbstractLeaderboardColumnOperation { public MoveLeaderboardColumnDown(String leaderboardName, String columnName) { - super(); - this.leaderboardName = leaderboardName; - this.columnName = columnName; + super(leaderboardName, columnName); } @Override public RacingEventService applyTo(RacingEventService toState) { - toState.moveLeaderboardColumnDown(leaderboardName, columnName); + toState.moveLeaderboardColumnDown(getLeaderboardName(), getColumnName()); return toState; } @Override - public RacingEventServiceOperation transformFor(RacingEventServiceOperation peerOp) { + public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { // TODO Auto-generated method stub return null; } + + + @Override + public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { + // TODO Auto-generated method stub + return null; + } + + + @Override + public RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard( + RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp) { + if (affectsSameColumn(removeColumnFromLeaderboardServerOp)) { + return ClientServerOperationPair.getNoOp(); + } else { + return removeColumnFromLeaderboardServerOp; + } + } + + + @Override + public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard( + RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) { + if (affectsSameColumn(removeColumnFromLeaderboardClientOp)) { + return ClientServerOperationPair.getNoOp(); + } else { + return removeColumnFromLeaderboardClientOp; + } + } + } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/MoveLeaderboardColumnUp.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/MoveLeaderboardColumnUp.java index 12972761eef..10c320785dd 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/MoveLeaderboardColumnUp.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/MoveLeaderboardColumnUp.java @@ -1,28 +1,55 @@ package com.sap.sailing.server.operationaltransformation; +import com.sap.sailing.operationaltransformation.ClientServerOperationPair; import com.sap.sailing.server.RacingEventService; -public class MoveLeaderboardColumnUp implements RacingEventServiceOperation { - private final String leaderboardName; - private final String columnName; +public class MoveLeaderboardColumnUp extends AbstractLeaderboardColumnOperation { public MoveLeaderboardColumnUp(String leaderboardName, String columnName) { - super(); - this.leaderboardName = leaderboardName; - this.columnName = columnName; + super(leaderboardName, columnName); } @Override public RacingEventService applyTo(RacingEventService toState) { - toState.moveLeaderboardColumnUp(leaderboardName, columnName); + toState.moveLeaderboardColumnUp(getLeaderboardName(), getColumnName()); return toState; } @Override - public RacingEventServiceOperation transformFor(RacingEventServiceOperation peerOp) { + public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { // TODO Auto-generated method stub return null; } + + + @Override + public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { + // TODO Auto-generated method stub + return null; + } + + + @Override + public RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard( + RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp) { + if (affectsSameColumn(removeColumnFromLeaderboardServerOp)) { + return ClientServerOperationPair.getNoOp(); + } else { + return removeColumnFromLeaderboardServerOp; + } + } + + + @Override + public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard( + RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) { + if (affectsSameColumn(removeColumnFromLeaderboardClientOp)) { + return ClientServerOperationPair.getNoOp(); + } else { + return removeColumnFromLeaderboardClientOp; + } + } + } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/OperationalTransformer.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/OperationalTransformer.java index a04f0cf0363..f752f840fcf 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/OperationalTransformer.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/OperationalTransformer.java @@ -8,8 +8,9 @@ public class OperationalTransformer implements Transformer transform(RacingEventServiceOperation clientOp, RacingEventServiceOperation serverOp) { - // TODO Auto-generated method stub - return null; + ClientServerOperationPair result = new ClientServerOperationPair( + clientOp.transformClientOp(serverOp), serverOp.transformServerOp(clientOp)); + return result; } } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RacingEventServiceOperation.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RacingEventServiceOperation.java index 082d145e4cf..5fdf9718407 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RacingEventServiceOperation.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RacingEventServiceOperation.java @@ -6,9 +6,34 @@ import com.sap.sailing.server.RacingEventService; public interface RacingEventServiceOperation extends Operation { /** - * Implements the specific transformation rule for the implementing subclass for the set of possible - * peer operations along which to transform this operation. See {@link Transformer#transform(Operation, Operation)} - * for the specification. + * Implements the specific transformation rule for the implementing subclass for the set of possible peer operations + * along which to transform this operation, assuming this is the client operation. See + * {@link Transformer#transform(Operation, Operation)} for the specification. + * + * @return the result of transforming this operation along serverOp */ - RacingEventServiceOperation transformFor(RacingEventServiceOperation peerOp); + RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp); + + /** + * Implements the specific transformation rule for the implementing subclass for the set of possible peer operations + * along which to transform this operation, assuming this is the server operation. See + * {@link Transformer#transform(Operation, Operation)} for the specification. + * + * @return the result of transforming this operation along clientOp + */ + RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp); + + /** + * Assumes this is the "server" operation and transforms the client's removeColumnFromLeaderboardClientOp according to this + * operation. The default implementation will probably pass on the untransformed client operation. However, if this + * operation deals with the leaderboard column being removed by removeColumnFromLeaderboardClientOp, + * the result will be null, meaning that this operation cannot be applied after the column has been removed. + */ + RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard(RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp); + + RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard(RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp); + + RacingEventServiceOperation transformClientRenameLeaderboardColumnOp(RenameLeaderboardColumn renameLeaderboardColumnClientOp); + + RacingEventServiceOperation transformServerRenameLeaderboardColumnOp(RenameLeaderboardColumn renameLeaderboardColumnServerOp); } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RemoveColumnFromLeaderboard.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RemoveColumnFromLeaderboard.java index defd8f57160..bbfd1b27d26 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RemoveColumnFromLeaderboard.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RemoveColumnFromLeaderboard.java @@ -1,27 +1,51 @@ package com.sap.sailing.server.operationaltransformation; +import com.sap.sailing.operationaltransformation.ClientServerOperationPair; import com.sap.sailing.server.RacingEventService; -public class RemoveColumnFromLeaderboard implements RacingEventServiceOperation { - private final String columnName; - private final String leaderboardName; +public class RemoveColumnFromLeaderboard extends AbstractLeaderboardColumnOperation { public RemoveColumnFromLeaderboard(String columnName, String leaderboardName) { - super(); - this.columnName = columnName; - this.leaderboardName = leaderboardName; + super(leaderboardName, columnName); } @Override public RacingEventService applyTo(RacingEventService toState) { - toState.removeLeaderboardColumn(leaderboardName, columnName); + toState.removeLeaderboardColumn(getLeaderboardName(), getColumnName()); return toState; } @Override - public RacingEventServiceOperation transformFor(RacingEventServiceOperation peerOp) { - // TODO Auto-generated method stub - return null; + public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { + return serverOp.transformClientRemoveColumnFromLeaderboard(this); } + + @Override + public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { + return clientOp.transformServerRemoveColumnFromLeaderboard(this); + } + + @Override + public RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard( + RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp) { + if (affectsSameColumn(removeColumnFromLeaderboardServerOp)) { + // skip server's remove and hence only apply the client's remove operation + return ClientServerOperationPair.getNoOp(); + } else { + return removeColumnFromLeaderboardServerOp; + } + } + + @Override + public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard( + RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) { + if (affectsSameColumn(removeColumnFromLeaderboardClientOp)) { + // skip client's remove and hence only apply the server's remove operation + return ClientServerOperationPair.getNoOp(); + } else { + return removeColumnFromLeaderboardClientOp; + } + } + } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RenameLeaderboardColumn.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RenameLeaderboardColumn.java index fba7f7ccce9..0c99cc70b03 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RenameLeaderboardColumn.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/RenameLeaderboardColumn.java @@ -1,30 +1,52 @@ package com.sap.sailing.server.operationaltransformation; +import com.sap.sailing.operationaltransformation.ClientServerOperationPair; import com.sap.sailing.server.RacingEventService; -public class RenameLeaderboardColumn implements RacingEventServiceOperation { - private final String leaderboardName; - private final String oldColumnName; +public class RenameLeaderboardColumn extends AbstractLeaderboardColumnOperation { private final String newColumnName; public RenameLeaderboardColumn(String leaderboardName, String oldColumnName, String newColumnName) { - super(); - this.leaderboardName = leaderboardName; - this.oldColumnName = oldColumnName; + super(leaderboardName, oldColumnName); this.newColumnName = newColumnName; } @Override public RacingEventService applyTo(RacingEventService toState) { - toState.renameLeaderboardColumn(leaderboardName, oldColumnName, newColumnName); + toState.renameLeaderboardColumn(getLeaderboardName(), getColumnName(), newColumnName); return toState; } + @Override + public RacingEventServiceOperation transformClientOp(RacingEventServiceOperation serverOp) { + return serverOp.transformClientRenameLeaderboardColumnOp(this); + } + @Override - public RacingEventServiceOperation transformFor(RacingEventServiceOperation peerOp) { - // TODO Auto-generated method stub - return null; + public RacingEventServiceOperation transformServerOp(RacingEventServiceOperation clientOp) { + return clientOp.transformServerRenameLeaderboardColumnOp(this); } + + @Override + public RacingEventServiceOperation transformServerRemoveColumnFromLeaderboard( + RemoveColumnFromLeaderboard removeColumnFromLeaderboardServerOp) { + if (affectsSameColumn(removeColumnFromLeaderboardServerOp)) { + return ClientServerOperationPair.getNoOp(); // don't need the rename anymore when column is removed + } else { + return removeColumnFromLeaderboardServerOp; + } + } + + @Override + public RacingEventServiceOperation transformClientRemoveColumnFromLeaderboard( + RemoveColumnFromLeaderboard removeColumnFromLeaderboardClientOp) { + if (affectsSameColumn(removeColumnFromLeaderboardClientOp)) { + return ClientServerOperationPair.getNoOp(); // don't need the rename anymore when column is removed + } else { + return removeColumnFromLeaderboardClientOp; + } + } + }