/** * Copyright 2009 Google Inc. * * Licensed 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.waveprotocol.wave.model.wave.undo; import org.waveprotocol.wave.model.operation.OperationPair; import org.waveprotocol.wave.model.operation.TransformException; import org.waveprotocol.wave.model.operation.wave.WaveletOperation; import org.waveprotocol.wave.model.operation.wave.WaveletOperationContext; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Buffers undoable operations, so that they can be withheld from CC and * reverted later. * */ public class OneStepBuffer { /** List of buffered undoable ops. */ private final List<WaveAggregateOp> undoable = new ArrayList<WaveAggregateOp>(); /** * Transforms a non undoable operation against the operations in the undo * stack. * * If updateUndoStack is true, also transform and update the undo stack, * otherwise, leave the undo stack untouched. * * @param op * @param updateUndoStack * @return returns the transformed nonundoable operation */ public List<WaveletOperation> transformNonUndoable(WaveletOperation op, boolean updateUndoStack) { if (!hasOperations() || OpUtils.isNoop(op)) { // If there are no buffered operations, or if the op is not important, // then we don't need to transform. return Collections.singletonList(op); } WaveAggregateOp nonUndoable = WaveAggregateOp.createAggregate(op); WaveAggregateOp composed = WaveAggregateOp.compose(undoable); final WaveAggregateOp transformedNonUndoable; final WaveAggregateOp transformedUndoable; try { OperationPair<WaveAggregateOp> transform = WaveAggregateOp.transform(nonUndoable, composed); transformedNonUndoable = transform.clientOp(); transformedUndoable = transform.serverOp(); } catch (TransformException e) { throw new RuntimeException("Transform exception while transforming nonUndoable", e); } // Update buffer undoable.clear(); if (updateUndoStack) { undoable.add(transformedUndoable); } else { // As an optimization, since we have composed the operations in the undo // stack, replace it with the composed op. undoable.add(composed); } WaveletOperationContext originalContext = op.getContext(); return transformedNonUndoable.toWaveletOperationsWithVersions(originalContext .getVersionIncrement(), originalContext.getHashedVersion()); } /** * Buffer an undoable operation * @param op */ public void undoable(WaveletOperation op) { undoable.add(WaveAggregateOp.createAggregate(op)); } /** * Flushes buffered operation by returning and clearing the buffer. */ public List<WaveletOperation> flush() { List<WaveletOperation> ret = new ArrayList<WaveletOperation>(); for (WaveAggregateOp op : undoable) { ret.addAll(op.toWaveletOperations()); } undoable.clear(); return ret; } /** * Reverts buffered operations by clearing the buffer and returning its * inverse. */ public List<WaveletOperation> revert() { WaveAggregateOp composed = WaveAggregateOp.compose(undoable); WaveAggregateOp invert = composed.invert(); undoable.clear(); return invert.toWaveletOperations(); } /** * Returns whether the buffer has operations. */ public boolean hasOperations() { return !undoable.isEmpty(); } }