/** * Copyright 2008 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.concurrencycontrol.client; import org.waveprotocol.wave.model.document.operation.DocOp; import org.waveprotocol.wave.model.document.operation.Nindo; import org.waveprotocol.wave.model.document.operation.SuperSink; import org.waveprotocol.wave.model.document.operation.DocOp.IsDocOp; import org.waveprotocol.wave.model.operation.OperationException; import org.waveprotocol.wave.model.operation.TransformException; import org.waveprotocol.wave.model.operation.wave.BlipContentOperation; import org.waveprotocol.wave.model.operation.wave.WaveletBlipOperation; import org.waveprotocol.wave.model.operation.wave.WaveletOperation; import org.waveprotocol.wave.model.operation.wave.WaveletOperationContext; import org.waveprotocol.wave.model.version.HashedVersion; import org.waveprotocol.wave.model.wave.ParticipantId; import java.util.ArrayList; import java.util.List; /** * Simple client mock that stores all the calls. * * @author zdwang@google.com (David Wang) */ public class ClientMock implements ConcurrencyControl.ConnectionListener { private int numOperationsReceived = 0; private final ArrayList<WaveletOperation> serverOperations = new ArrayList<WaveletOperation>(); private final ArrayList<WaveletOperation> clientOperations = new ArrayList<WaveletOperation>(); private final SuperSink doc; private final ConcurrencyControl cc; // Client ops are compared via context, so we need to create a participant to identify // client ops. private final ParticipantId participantId; private final ServerConnectionMock connection; /** * Construct a mock without any docs. */ public ClientMock(ConcurrencyControl cc) { this(cc, null, null, null); } /** * Given the doc and applier, this mock will try to apply the client ops on * trigger and apply the server ops on receive. * * @param doc */ public ClientMock(ConcurrencyControl cc, SuperSink doc, ParticipantId participantId, ServerConnectionMock connection) { this.cc = cc; this.doc = doc; this.participantId = participantId; this.connection = connection; } private void onServerOperation(WaveletOperation operation) { serverOperations.add(operation); try { if (doc != null) { applyOperation(operation); } } catch (OperationException e) { throw new RuntimeException("OperationException", e); } } /** * Gets the operations from concurrency control. */ public void receiveServerOperations() { WaveletOperation op; while ((op = cc.receive()) != null) { onServerOperation(op); } } /** * Gets the reconnection versions from concurrency control. */ public List<HashedVersion> getReconnectionVersions() { return cc.getReconnectionVersions(); } /** * @return Operations we got from {@link #onServerOperation(WaveletOperation)}. */ public ArrayList<WaveletOperation> getServerOperations() { return serverOperations; } /** * Clears stored ops we got from {@link #onServerOperation(WaveletOperation)}. */ public void clearServerOperations() { serverOperations.clear(); } /** * Applies an insertion to the document. */ public void doInsert(int offset, String chars) throws OperationException { Nindo nindo = Nindo.insertCharacters(offset, chars); DocOp op = doc.consumeAndReturnInvertible(nindo); BlipContentOperation blipOp = new BlipContentOperation( new WaveletOperationContext(participantId, 0L, 1), op); WaveletBlipOperation wop = new WaveletBlipOperation("blip id", blipOp); clientOperations.add(wop); } /** * Pretend a client did the given op, so that when we call flush, the operation will * be ejected. */ public void addClientOperation(WaveletOperation operation) throws OperationException { clientOperations.add(operation); if (doc != null) { applyOperation(operation); } } /** * Applies the given operation to the doc. * * @param operation * @throws OperationException */ private void applyOperation(WaveletOperation operation) throws OperationException { if (operation instanceof WaveletBlipOperation) { WaveletBlipOperation waveOp = (WaveletBlipOperation) operation; if (waveOp.getBlipOp() instanceof BlipContentOperation) { BlipContentOperation blipOp = (BlipContentOperation) waveOp.getBlipOp(); doc.consume(blipOp.getContentOp()); } } } /** * Flush the client's operation to cc. */ public void flush() throws TransformException { cc.onClientOperations(clientOperations.toArray(new WaveletOperation[] {})); clientOperations.clear(); } /** * @return the doc */ public IsDocOp getDoc() { return doc; } /** * @return participant id for this client. */ public ParticipantId getParticipantId() { return participantId; } /** * Get connection to the server. */ public ServerConnectionMock getConnection() { return connection; } @Override public void onOperationReceived() { numOperationsReceived++; } /** * @return the count of events from cc that we have received */ public int getNumOpsReceived() { return numOperationsReceived; } /** * Resets the number of ops received and the recovery failure flag. */ public void clearEvents() { numOperationsReceived = 0; } }