/** * 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.model.wave.opbased; import org.waveprotocol.wave.model.document.Document; import org.waveprotocol.wave.model.document.MutableDocument; import org.waveprotocol.wave.model.document.operation.DocOp; import org.waveprotocol.wave.model.operation.OperationException; import org.waveprotocol.wave.model.operation.SilentOperationSink; import org.waveprotocol.wave.model.operation.wave.BlipContentOperation; import org.waveprotocol.wave.model.operation.wave.BlipOperation; import org.waveprotocol.wave.model.operation.wave.SubmitBlip; import org.waveprotocol.wave.model.operation.wave.WaveletBlipOperation; import org.waveprotocol.wave.model.wave.Blip; import org.waveprotocol.wave.model.wave.ParticipantId; import org.waveprotocol.wave.model.wave.data.BlipData; import java.util.Collections; import java.util.Set; /** * Implements the {@code Blip} interface by translating its methods into * operation objects which are applied to a {@link BlipData}. Refer to comment * in {@link OpBasedWavelet} for an explanation of how the adapters fit * together. * * NOTE(user): this adapter consumes operations produced locally by the blip * content's document adapter by boxing it as a blip operation and passing it to * the wavelet adapter. The second boxing from a blip op to a wavelet op is done * here rather than in the wavelet adapter because this adapter already knows * the blip id, and means that the wavelet adapter can have a single sink for * all (boxed) blip ops, rather than a sink per blip. * * @see OpBasedWavelet */ public class OpBasedBlip implements Blip { /** Primitive-view of the adapted blip. */ private final BlipData blip; /** Adapter of the wavelet in which this blip appears, with which this adapter collaborates. */ private final OpBasedWavelet wavelet; /** Sink to which produced operations are sent for remote notification. */ private final SilentOperationSink<WaveletBlipOperation> outputSink; /** * Creates a blip adapter. * * @param blip primitive-view of a blip * @param wavelet adapter of the wavelet in which the primitive blip appears * @param outputSink sink to which operations produced by this adapters should be sent. */ public OpBasedBlip(BlipData blip, OpBasedWavelet wavelet, SilentOperationSink<WaveletBlipOperation> outputSink) { this.blip = blip; this.wavelet = wavelet; this.outputSink = outputSink; blip.getContent().init(new SilentOperationSink<DocOp>() { public void consume(DocOp op) { OpBasedBlip.this.consume(op); } }); } /** * Implements the strategy for consuming operations sent to this adapter from * a document adapter, after the operation has already been applied locally. A * received operation is boxed as a blip operation, then used to update the * primitive blip, then boxed as a wave operation and sent to the wavelet * adapter. */ private void consume(DocOp op) { // Box as blip op, and update local blip BlipContentOperation blipOp = new BlipContentOperation(wavelet.createContext(), op); blipOp.update(OpBasedBlip.this.blip); // Box as wavelet op, and pass to wavelet adapter outputSink.consume(new WaveletBlipOperation(getId(), blipOp)); } /** * Applies the op to the adapted wavelet and then outputs the op for remote notification * * @param op The op to apply */ private void applyAndSend(BlipOperation op) { // Apply locally try { op.apply(blip); } catch (OperationException e) { wavelet.handleException(e); return; } // Pass to wave outputSink.consume(new WaveletBlipOperation(blip.getId(), op)); } // // Mutator to operation translations // @Override public void submit() { applyAndSend(new SubmitBlip(wavelet.createContext())); } // // Adapted accessors // /** * Adapts the primitive-blip's document-operation sink as a {@link MutableDocument}. */ @Override public Document getContent() { return blip.getContent().getMutableDocument(); } // // Vanilla accessors. // @Override public OpBasedWavelet getWavelet() { return wavelet; } @Override public ParticipantId getAuthorId() { return blip.getAuthor(); } @Override public Set<ParticipantId> getContributorIds() { return Collections.unmodifiableSet(blip.getContributors()); } @Override public Long getLastModifiedTime() { return blip.getLastModifiedTime(); } @Override public Long getLastModifiedVersion() { return blip.getLastModifiedVersion(); } @Override public String getId() { return blip.getId(); } @Override public int hashCode() { return 37 + blip.getId().hashCode(); } @Override public boolean equals(Object obj) { if (this == obj) { return true; } else if (!(obj instanceof Blip)) { return false; } else { return getId().equals(((Blip) obj).getId()); } } @Override public String toString() { return "OpBasedBlip { " + blip + " }"; } }