/**
* 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.operation.wave;
import org.waveprotocol.wave.model.util.CollectionUtils;
import org.waveprotocol.wave.model.util.Preconditions;
import org.waveprotocol.wave.model.version.HashedVersion;
import org.waveprotocol.wave.model.wave.ParticipantId;
import java.util.AbstractList;
import java.util.Collections;
import java.util.List;
/**
* A transformed wavelet delta is the result of a {@link WaveletDelta} being
* applied at the server.
*
* @author anorth@google.com (Alex North)
*/
public final class TransformedWaveletDelta extends AbstractList<WaveletOperation> {
/** Author of the operations. */
private final ParticipantId author;
/** Wavelet version after the delta applied. */
private final HashedVersion resultingVersion;
/** Timestamp when the delta was applied. */
private final long applicationTimestamp;
/** List of operations in the order they were applied. */
private final List<WaveletOperation> ops;
/**
* Clones ops from a client delta to a transformed delta, replacing their
* contexts with that implied by the delta's metadata.
*/
public static TransformedWaveletDelta cloneOperations(HashedVersion resultingVersion,
long applicationTimestamp, WaveletDelta delta) {
return cloneOperations(delta.getAuthor(), resultingVersion, applicationTimestamp, delta);
}
/**
* Clones a list of operations into a transformed delta, replacing their
* contexts with that implied by the delta's metadata.
*/
public static TransformedWaveletDelta cloneOperations(ParticipantId author,
HashedVersion resultingVersion, long applicationTimestamp,
List<? extends WaveletOperation> ops) {
List<WaveletOperation> transformedOps = CollectionUtils.newArrayList();
WaveletOperationContext initialContext =
new WaveletOperationContext(author, applicationTimestamp, 1);
for (int i = 0; i < ops.size() - 1; ++i) {
transformedOps.add(WaveletOperation.cloneOp(ops.get(i), initialContext));
}
if (ops.size() > 0) {
WaveletOperationContext finalContext =
new WaveletOperationContext(author, applicationTimestamp, 1, resultingVersion);
transformedOps.add(WaveletOperation.cloneOp(ops.get(ops.size() - 1), finalContext));
}
return new TransformedWaveletDelta(author, resultingVersion, applicationTimestamp,
transformedOps);
}
/**
* Create new delta from an author and a sequence of operations.
*
* @param author of the operations
* @param resultingVersion hashed version after the delta applied
* @param applicationTimestamp timestamp at which the delta applied
* @param ops operations
*/
public TransformedWaveletDelta(ParticipantId author, HashedVersion resultingVersion,
long applicationTimestamp, Iterable<? extends WaveletOperation> ops) {
this.author = author;
this.resultingVersion = resultingVersion;
this.applicationTimestamp = applicationTimestamp;
this.ops = Collections.unmodifiableList(CollectionUtils.newArrayList(ops));
// Check that the op contexts are right. (Everything in there is
// redundant...)
for (int i = 0; i < this.ops.size(); i++) {
WaveletOperationContext c = this.ops.get(i).getContext();
Preconditions.checkArgument(c.getCreator().equals(author),
"Context creator %s doesn't match delta author %s", c.getCreator(), author);
Preconditions.checkArgument(c.getVersionIncrement() == 1,
"Invalid context version increment %s", c.getVersionIncrement());
Preconditions.checkArgument(c.hasHashedVersion() == (i == this.ops.size() - 1),
"[Un]expected hashed version on op %s of %s", i + 1, this.ops.size());
Preconditions.checkArgument((i != this.ops.size() - 1)
|| c.getHashedVersion().equals(resultingVersion),
"Context hashed version %s doesn't match delta hashed version %s", c.getHashedVersion(),
resultingVersion);
Preconditions.checkArgument(c.getTimestamp() == applicationTimestamp,
"Context timestamp %s doesn't match delta timestamp %s",
c.getTimestamp(), applicationTimestamp);
}
}
/** Returns the author of the delta. */
public ParticipantId getAuthor() {
return author;
}
/** Returns the wavelet version to which the delta applied. */
public long getAppliedAtVersion() {
return resultingVersion.getVersion() - ops.size();
}
/** Returns the wavelet version after the delta applied. */
public HashedVersion getResultingVersion() {
return resultingVersion;
}
/** Returns the timestamp when the delta was applied. */
public long getApplicationTimestamp() {
return applicationTimestamp;
}
@Override
public int size() {
return ops.size();
}
@Override
public WaveletOperation get(int index) {
return ops.get(index);
}
@Override
public int hashCode() {
int result = 17;
result = 31 * result + author.hashCode();
result = 31 * result + resultingVersion.hashCode();
result = 31 * result + Long.valueOf(applicationTimestamp).hashCode();
for (WaveletOperation op : ops) {
result = 31 * result + op.hashCode();
}
return result;
}
@Override
public boolean equals(Object obj) {
if (obj == this) {
return true;
} else if (obj instanceof TransformedWaveletDelta) {
TransformedWaveletDelta wd = (TransformedWaveletDelta) obj;
return author.equals(wd.author)
&& resultingVersion.equals(wd.resultingVersion)
&& (applicationTimestamp == wd.applicationTimestamp)
&& ops.equals(wd.ops);
} else {
return false;
}
}
@Override
public String toString() {
StringBuilder builder = new StringBuilder();
builder.append("TransformedWaveletDelta(").append(author).append(", ");
builder.append(resultingVersion).append(", ");
builder.append(applicationTimestamp).append(" -> ");
if (ops.isEmpty()) {
builder.append("[]");
} else {
builder.append(ops.size()).append(" ops: [").append(ops.get(0));
for (int i = 1; i < ops.size(); i++) {
builder.append(",").append(ops.get(i));
}
builder.append("]");
}
builder.append(")");
return builder.toString();
}
}