/** * 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.operation.wave; import org.waveprotocol.wave.model.wave.data.BlipData; import org.waveprotocol.wave.model.document.operation.DocOp; import org.waveprotocol.wave.model.document.operation.algorithm.DocOpInverter; import org.waveprotocol.wave.model.operation.OpComparators; import org.waveprotocol.wave.model.operation.OperationException; import org.waveprotocol.wave.model.util.Preconditions; import java.util.Collections; import java.util.List; /** * Operation class for boxing a document operation as a wave/blip operation. * A {@code BlipContentOperation} applies to a blip by passing its contained document operation to * the blip's document-operation sink. Note that this is slightly different from what one might * expect (applying to a blip by applying the contained document-operation to the blip's content), * but it leverages the weaker contract of a sink in order to hide the document structure from the * blip interface such that a wider range of implementation options are possible for the reception * of document operations. * */ public final class BlipContentOperation extends BlipOperation { /** Document operation to apply to the target blip's content. */ private final DocOp contentOp; private final UpdateContributorMethod method; /** * Constructs a blip-content operation. * * @param context operation context * @param contentOp document operation to apply to the target blip */ public BlipContentOperation(WaveletOperationContext context, DocOp contentOp) { this(context, contentOp, UpdateContributorMethod.ADD); } /** * Constructs a blip-content operation. * * @param context operation context * @param contentOp document operation to apply to the target blip * @param update contributor update method */ BlipContentOperation(WaveletOperationContext context, DocOp contentOp, UpdateContributorMethod update) { super(context, WorthyChangeChecker.isWorthy(contentOp)); Preconditions.checkNotNull(contentOp, "Null document mutation"); this.contentOp = contentOp; this.method = update; } /** * Applies this operation to a blip by applying its document operation to the blip's content. */ @Override protected void doApply(BlipData blip) throws OperationException { // Apply document mutation blip.getContent().consume(contentOp); if (isWorthyOfAttribution(blip.getId())) { blip.onRemoteContentModified(); } } @Override protected void doUpdate(BlipData target) { update(target, method); } @Override public List<? extends BlipOperation> applyAndReturnReverse(final BlipData blip) throws OperationException { WaveletOperationContext reverseContext = createReverseContext(blip); // Update metadata UpdateContributorMethod reverseMethod = update(blip, method); blip.getContent().consume(contentOp); DocOp reverseContentOp = DocOpInverter.invert(contentOp); BlipContentOperation reverseOp = new BlipContentOperation(reverseContext, reverseContentOp, reverseMethod); return Collections.singletonList(reverseOp); } @Override public void acceptVisitor(BlipOperationVisitor visitor) { visitor.visitBlipContentOperation(this); } /** * Updates only the metadata of a blip. * * @param target blip to update */ public void update(BlipData target) { doUpdate(target); } /** * Gets the contained document operation. * * @return the contained document operation. */ public DocOp getContentOp() { return contentOp; } @Override public boolean updatesBlipMetadata(String blipId) { return isWorthyOfAttribution(blipId); } @Override public String toString() { return "document op: " + contentOp; } @Override public int hashCode() { // Note that we don't have an implementation of contentOp.hashCode() // which is compatible with OpComparators.SYNTACTIC_IDENTITY.equal(). // Therefore we ignore contentOp in the hash code computation here // so that it's compatible with equals(). // TODO: Implement contentOp.hashCode(), compatible with // OpComparators.SYNTACTIC_IDENTITY.equal(), and return that here. return 0; } @Override public boolean equals(Object obj) { /* * NOTE(user): We're ignoring context and update method in equality * comparison. The plan is to remove context from all operations in the * future. */ if (!(obj instanceof BlipContentOperation)) { return false; } BlipContentOperation other = (BlipContentOperation) obj; return OpComparators.SYNTACTIC_IDENTITY.equal(contentOp, other.contentOp); } }