Package org.waveprotocol.wave.model.wave.undo

Source Code of org.waveprotocol.wave.model.wave.undo.WaveAggregateOp$OpCreatorPair

/**
* Copyright 2010 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 com.google.common.annotations.VisibleForTesting;

import org.waveprotocol.wave.model.document.operation.DocOp;
import org.waveprotocol.wave.model.operation.OperationPair;
import org.waveprotocol.wave.model.operation.TransformException;
import org.waveprotocol.wave.model.operation.core.CoreAddParticipant;
import org.waveprotocol.wave.model.operation.core.CoreRemoveParticipant;
import org.waveprotocol.wave.model.operation.core.CoreWaveletDocumentOperation;
import org.waveprotocol.wave.model.operation.core.CoreWaveletOperation;
import org.waveprotocol.wave.model.operation.wave.AddParticipant;
import org.waveprotocol.wave.model.operation.wave.BlipContentOperation;
import org.waveprotocol.wave.model.operation.wave.RemoveParticipant;
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.util.Preconditions;
import org.waveprotocol.wave.model.version.HashedVersion;
import org.waveprotocol.wave.model.wave.ParticipantId;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
* An aggregate operation similar to @see AggregateOperation, but with an
* additional field to specify the creator of its component ops.
*
*/
class WaveAggregateOp {
  /**  List of op, creator pairs */
  private final List<OpCreatorPair> opPairs;

  private static class OpCreatorPair {
    final ParticipantId creator;
    final AggregateOperation op;

    OpCreatorPair(AggregateOperation op, ParticipantId creator) {
      Preconditions.checkNotNull(op, "op must be non-null");
      Preconditions.checkNotNull(creator, "creator must be non-null");
      this.op = op;
      this.creator = creator;
    }
  }

  /**
   * Constructs a WaveAggregateOp from a Wavelet op.
   * @param op
   */
  static WaveAggregateOp createAggregate(WaveletOperation op) {
    Preconditions.checkNotNull(op, "op must be non-null");
    Preconditions.checkNotNull(op.getContext(), "context must be non-null");

    ParticipantId creator = op.getContext().getCreator();
    AggregateOperation aggOp = AggregateOperation.createAggregate(op);

    return new WaveAggregateOp(aggOp, creator);
  }

  /**
   * Compose a list of WaveAggregateOps.
   *
   * NOTE(user): Consider adding some checks for operations that span different
   * creators, i.e. a compose of addParticipant(personA) by creator1 and
   * creator2 should be invalid.
   *
   * @param operations
   */
  static WaveAggregateOp compose(List<WaveAggregateOp> operations) {
    return new WaveAggregateOp(composeDocumentOps(flatten(operations)));
  }

  /**
   * Transform the given operations.
   *
   * @param clientOp
   * @param serverOp
   *
   * @throws TransformException
   */
  static OperationPair<WaveAggregateOp> transform(WaveAggregateOp clientOp,
      WaveAggregateOp serverOp) throws TransformException {
    // This gets filled with transformed server ops.
    List<OpCreatorPair> transformedServerOps = new ArrayList<OpCreatorPair>();
    // This starts with the original client ops, and gets transformed with each server op.
    List<OpCreatorPair> transformedClientOps = new ArrayList<OpCreatorPair>(clientOp.opPairs);

    for (OpCreatorPair sPair : serverOp.opPairs) {
      transformedServerOps.add(transformAndUpdate(transformedClientOps, sPair));
    }

    return new OperationPair<WaveAggregateOp>(new WaveAggregateOp(transformedClientOps),
        new WaveAggregateOp(transformedServerOps));
  }

  private static void maybeCollectOps(List<AggregateOperation> ops, ParticipantId creator,
      List<OpCreatorPair> dest) {
    if (ops != null && ops.size() > 0) {
      assert creator != null;
      dest.add(new OpCreatorPair(AggregateOperation.compose(ops), creator));
    }
  }

  private static List<OpCreatorPair> composeDocumentOps(List<OpCreatorPair> ops) {
    List<OpCreatorPair> ret = new ArrayList<OpCreatorPair>();
    ParticipantId currentCreator = null;
    List<AggregateOperation> currentOps = null;

    // Group sequences of ops under same creator.
    for (OpCreatorPair op : ops) {
      if (!op.creator.equals(currentCreator)) {
        // If the creator is different, compose and finish with the current
        // group, and start the next group.
        maybeCollectOps(currentOps, currentCreator, ret);
        currentOps = null;
        currentCreator = op.creator;
      }

      if (currentOps == null) {
        currentOps =  new ArrayList<AggregateOperation>();
      }
      currentOps.add(op.op);
    }
    // Collect the last batch of ops.
    maybeCollectOps(currentOps, currentCreator, ret);

    return ret;
  }

  /**
   * Flatten a sequence of WaveAggregate operations into a list of
   * OpCreatorPairs.
   *
   * @param operations
   */
  private static List<OpCreatorPair> flatten(List<WaveAggregateOp> operations) {
    List<OpCreatorPair> ret = new ArrayList<OpCreatorPair>();
    for (WaveAggregateOp aggOp : operations) {
      ret.addAll(aggOp.opPairs);
    }
    return ret;
  }

  /**
   * Transform stream S against streamC, updating streamC and returning the
   * transform of s.
   *
   * @param streamC
   * @param s
   * @throws TransformException
   */
  private static OpCreatorPair transformAndUpdate(List<OpCreatorPair> streamC, OpCreatorPair s)
      throws TransformException {
    // Makes a copy of streamC and clear the original, so that it can be filled with the
    // transformed version.
    List<OpCreatorPair> streamCCopy = new ArrayList<OpCreatorPair>(streamC);
    streamC.clear();

    for (OpCreatorPair c : streamCCopy) {
      OperationPair<OpCreatorPair> transformed = transform(c, s);
      streamC.add(transformed.clientOp());
      s = transformed.serverOp();
    }

    return s;
  }

  private static OperationPair<OpCreatorPair> transform(OpCreatorPair c, OpCreatorPair s)
      throws TransformException {
    OperationPair<AggregateOperation> transformed = AggregateOperation.transform(c.op, s.op);
    return new OperationPair<OpCreatorPair>(new OpCreatorPair(transformed.clientOp(), c.creator),
        new OpCreatorPair(transformed.serverOp(), s.creator));
  }

  @VisibleForTesting
  WaveAggregateOp(AggregateOperation op, ParticipantId creator) {
    opPairs = Collections.singletonList(new OpCreatorPair(op, creator));
  }

  private WaveAggregateOp(List<OpCreatorPair> pairs) {
    Preconditions.checkNotNull(pairs, "pairs must be non-null");
    this.opPairs = pairs;
  }

  /**
   * @return wavelet operations corresponding to this WaveAggregateOp.
   */
  public List<WaveletOperation> toWaveletOperations() {
    return toWaveletOperationsWithVersions(0, null);
  }

  /**
   * Special case where we populate the last op in the list with the given versions.
   * This is necessary to preserve the WaveletOperationContext from the server.
   *
   * @param versionIncrement
   * @param hashedVersion
   */
  public List<WaveletOperation> toWaveletOperationsWithVersions(long versionIncrement,
      HashedVersion hashedVersion) {
    List<WaveletOperation> ret = new ArrayList<WaveletOperation>();
    for (int i = 0; i < opPairs.size(); ++i) {
      OpCreatorPair pair = opPairs.get(i);
      boolean isLastOfOuter = (i == opPairs.size() - 1);

      List<CoreWaveletOperation> coreWaveletOperations = pair.op.toCoreWaveletOperations();

      for (int j = 0; j < coreWaveletOperations.size(); ++j) {
        boolean isLast = isLastOfOuter && (j == coreWaveletOperations.size() - 1);
        WaveletOperationContext opContext =
            contextForCreator(pair.creator, versionIncrement, hashedVersion, isLast);
        WaveletOperation waveletOps =
            coreWaveletOpsToWaveletOps(coreWaveletOperations.get(j), opContext);
        ret.add(waveletOps);
      }
    }
    return ret;
  }

  WaveAggregateOp invert() {
    List<OpCreatorPair> invertedPairs = new ArrayList<OpCreatorPair>();
    for (OpCreatorPair pair : opPairs) {
      invertedPairs.add(new OpCreatorPair(pair.op.invert(), pair.creator));
    }
    Collections.reverse(invertedPairs);
    return new WaveAggregateOp(invertedPairs);
  }

  private WaveletOperation coreWaveletOpsToWaveletOps(CoreWaveletOperation op,
      WaveletOperationContext context) {
    if (op instanceof CoreRemoveParticipant) {
      ParticipantId participantId = ((CoreRemoveParticipant) op).getParticipantId();
      return new RemoveParticipant(context, participantId);
    } else if (op instanceof CoreAddParticipant) {
      ParticipantId participantId = ((CoreAddParticipant) op).getParticipantId();
      return new AddParticipant(context, participantId);
    } else if (op instanceof CoreWaveletDocumentOperation) {
      CoreWaveletDocumentOperation waveletDocOp = (CoreWaveletDocumentOperation) op;
      String documentId = waveletDocOp.getDocumentId();
      DocOp operation = waveletDocOp.getOperation();
      return new WaveletBlipOperation(documentId, new BlipContentOperation(context, operation));
    }

    throw new RuntimeException("unhandled operation type");
  }

  /**
   * @param creator
   * @param isLastOfSeq
   * @param hashedVersion
   * @param versionIncrement
   * @return a WaveletOperationContext with the specified participant as the
   *         creator.
   */
  private WaveletOperationContext contextForCreator(ParticipantId creator, long versionIncrement,
      HashedVersion hashedVersion, boolean isLastOfSeq) {
    if (isLastOfSeq) {
      return new WaveletOperationContext(creator, System.currentTimeMillis(), versionIncrement,
          hashedVersion);
    } else {
      // NOTE(user): The timestamp and version field are not relevant in the
      // client for outgoing ops, but may need to be filled out properly on the
      // server.
      return new WaveletOperationContext(creator, System.currentTimeMillis(), 0L);
    }
  }
}
TOP

Related Classes of org.waveprotocol.wave.model.wave.undo.WaveAggregateOp$OpCreatorPair

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.