Package com.google.walkaround.slob.client

Source Code of com.google.walkaround.slob.client.ChannelTestUtil$Package

/*
* Copyright 2011 Google Inc. All Rights Reserved.
*
* 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 com.google.walkaround.slob.client;

import com.google.common.collect.ImmutableList;
import com.google.common.collect.Lists;

import org.waveprotocol.wave.model.document.indexed.IndexedDocument;
import org.waveprotocol.wave.model.document.operation.Automatons;
import org.waveprotocol.wave.model.document.operation.DocOp;
import org.waveprotocol.wave.model.document.operation.algorithm.DocOpCollector;
import org.waveprotocol.wave.model.document.operation.algorithm.DocOpInverter;
import org.waveprotocol.wave.model.document.operation.algorithm.Transformer;
import org.waveprotocol.wave.model.document.operation.automaton.AutomatonDocument;
import org.waveprotocol.wave.model.document.operation.impl.DocOpUtil;
import org.waveprotocol.wave.model.document.raw.impl.Element;
import org.waveprotocol.wave.model.document.raw.impl.Node;
import org.waveprotocol.wave.model.document.raw.impl.Text;
import org.waveprotocol.wave.model.document.util.DocProviders;
import org.waveprotocol.wave.model.operation.OperationException;
import org.waveprotocol.wave.model.operation.OperationPair;
import org.waveprotocol.wave.model.operation.OperationRuntimeException;
import org.waveprotocol.wave.model.operation.TransformException;
import org.waveprotocol.wave.model.testing.RandomDocOpGenerator;
import org.waveprotocol.wave.model.testing.RandomDocOpGenerator.Parameters;
import org.waveprotocol.wave.model.testing.RandomDocOpGenerator.RandomProvider;

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

/**
* @author danilatos@google.com (Daniel Danilatos)
*/
public final class ChannelTestUtil {
  private ChannelTestUtil(){}

  private static final TransformQueue.Transformer<DocOp> transformer =
      new TransformQueue.Transformer<DocOp>() {
        @Override
        public OperationPair<DocOp> transform(DocOp clientOp, DocOp serverOp) {
          try {
            return Transformer.transform(clientOp, serverOp);
          } catch (TransformException e) {
            throw new RuntimeException(e);
          }
        }

        @Override
        public List<DocOp> compact(List<DocOp> clientOps) {
          if (clientOps.isEmpty()) {
            return clientOps;
          }
          DocOpCollector c = new DocOpCollector();
          for (DocOp op : clientOps) {
            c.add(op);
          }
          return ImmutableList.of(c.composeAll());
        }
      };

  public static class SometimesCompactingTransformer implements TransformQueue.Transformer<DocOp> {
    /** Set this to control compacting behaviour */
    public boolean compact = true;

    @Override
    public OperationPair<DocOp> transform(DocOp clientOp, DocOp serverOp) {
      return transformer.transform(clientOp, serverOp);
    }

    @Override
    public List<DocOp> compact(List<DocOp> clientOps) {
      if (compact) {
        return transformer.compact(clientOps);
      } else {
        return clientOps;
      }
    }
  }

  public static final TransformQueue.Transformer<DocOp> randomlyCompactingTransformer(
      final int seed) {
    return new TransformQueue.Transformer<DocOp>() {
      final Random r = new Random(seed);

      @Override
      public OperationPair<DocOp> transform(DocOp clientOp, DocOp serverOp) {
        return transformer.transform(clientOp, serverOp);
      }

      @Override
      public List<DocOp> compact(List<DocOp> clientOps) {
        if (r.nextBoolean()) {
          return clientOps;
        } else {
          return transformer.compact(clientOps);
        }
      }
    };
  }



  /**
   * Convenience wrapper around a document
   */
  static class Doc {
    final IndexedDocument<Node, Element, Text> doc = DocProviders.POJO.parse("");
    final AutomatonDocument autoDoc = Automatons.fromReadable(doc);
    void consume(DocOp op) throws OperationException {
      doc.consume(op);
    }
    Doc copy() {
      Doc copy = new Doc();
      try {
        copy.consume(doc.asOperation());
        return copy;
      } catch (OperationException e) {
        throw new OperationRuntimeException("Invalid other document", e);
      }
    }
    String repr() {
      return DocOpUtil.toXmlString(doc.asOperation());
    }
  }

  /**
   * Represents a message on the wire.
   */
  static class Package {
    /**
     * Whether the op originated from the client. This can be true even after
     * the op has gone through the server and been transformed, and sent back
     * to the client from the server.
     */
    final boolean clientSourced;
    final int appliesToRevision;
    final int resultingRevision;
    final ImmutableList<DocOp> ops;


    public Package(int appliesToRevision, boolean clientSourced, List<DocOp> ops) {
      this.appliesToRevision = appliesToRevision;
      this.resultingRevision = appliesToRevision + 1;
      this.clientSourced = clientSourced;
      this.ops = ImmutableList.copyOf(ops);
    }

    @Override public String toString() {
      return "[" + (clientSourced ? "C" : "S") + appliesToRevision + ", " + ops.size() + ops + "]";
    }
  }

  /** Restricts to one op in the package */
  static class UnitPackage extends Package {
    public UnitPackage(int appliesToRevision, boolean clientSourced, DocOp op) {
      super(appliesToRevision, clientSourced, Collections.singletonList(op));
    }

    DocOp onlyOp() {
      assert ops.size() == 1;
      return ops.get(0);
    }
  }

  /**
   * Simulation of the server and the internet between the client and server.
   */
  static class FakeServer {
    private final RandomProvider r;
    private final Parameters p;
    FakeServer(RandomProvider r, Parameters p) {
      this.r = r;
      this.p = p;
    }

    Doc doc = new Doc();
    int nextSendAppliesAtRevision = 0;
    int lastClientAppliedAtRevision = 0;
    /** Start of the history log */
    int baseVersion = 0;
    /** Current server revision */
    int endRevision = 0;
    /** Recent operation history */
    ArrayList<UnitPackage> history = new ArrayList<UnitPackage>();
    /** Operations in flight down the browserchannel to the client */
    LinkedList<UnitPackage> browserchannel = new LinkedList<UnitPackage>();
    /** Operations in flight on an xhr to the server */
    Package xhr;

    @Override public String toString() {
      return "Server{ " + baseVersion + " - " + endRevision + "\n  " +
          doc +
          "\n  h:" + history +
          "\n  b:" + browserchannel.size() + browserchannel +
          "\n  x:" + xhr + "\n}";
    }

    void sendToServer(int revision, List<DocOp> ops) {
      xhr = new Package(revision, true, Lists.newArrayList(ops));
    }

    /**
     * @return true if there was an op to send
     */
    boolean sendToClient() {
      assert nextSendAppliesAtRevision <= endRevision;
      if (nextSendAppliesAtRevision == endRevision) {
        return false;
      }
      browserchannel.add(opAppliedAt(nextSendAppliesAtRevision));
      nextSendAppliesAtRevision++;
      return true;
    }

    boolean channelPending() {
      return !browserchannel.isEmpty();
    }

    UnitPackage clientReceive() {
      return browserchannel.remove();
    }

    /**
     * @return true if there was a pending xhr request
     */
    boolean serverReceive() {
      if (xhr == null) {
        return false;
      }

      int appliesToRev = xhr.appliesToRevision;
      assert xhr.clientSourced;
      lastClientAppliedAtRevision = appliesToRev;

      List<DocOp> ops = Lists.newArrayList(xhr.ops);
      assert ops.size() > 0;
      xhr = null;

      for (int i = appliesToRev; i < endRevision; i++) {
        DocOp serverOp = opAppliedAt(i).onlyOp();
        for (int j = 0; j < ops.size(); j++) {
          OperationPair<DocOp> pair = transformer.transform(ops.get(j), serverOp);
          serverOp = pair.serverOp();
          ops.set(j, pair.clientOp());
        }
      }

      for (DocOp op : ops) {
        apply(op, true);
      }

      return true;
    }

    UnitPackage opAppliedAt(int revision) {
      return history.get(revision - baseVersion);
    }

    void randomServerOp() {
      apply(RandomDocOpGenerator.generate(r, p, doc.autoDoc), false);
    }

    void apply(DocOp op, boolean clientSourced) {
      try {
        doc.consume(op);
      } catch (OperationException e) {
        throw new RuntimeException(e);
      }
      history.add(new UnitPackage(endRevision, clientSourced, op));
      endRevision++;

      while (sendToClient()){}

      if (endRevision % 50 == 0) {
        discardOldHistory();
      }

      checkState();
    }

    Doc at(int historicalRevision) {
      assert historicalRevision >= 0 && historicalRevision <= endRevision;
      Doc copy = doc.copy();
      int rev = endRevision - 1;
      for (rev = endRevision - 1; rev >= historicalRevision; rev--) {
        try {
          copy.consume(DocOpInverter.invert(opAppliedAt(rev).onlyOp()));
        } catch (OperationException e) {
          throw new RuntimeException(e);
        }
      }
      return copy;
    }

    void discardOldHistory() {
      int opsToDiscard =
          Math.min(lastClientAppliedAtRevision, nextSendAppliesAtRevision) - baseVersion;
      baseVersion += opsToDiscard;
      history = Lists.newArrayList(history.subList(opsToDiscard, history.size()));
    }

    void checkState() {
      assert endRevision == history.size() + baseVersion;
    }

    int revision() {
      return endRevision;
    }

    boolean xhrInFlight() {
      return xhr != null;
    }
  }
}
TOP

Related Classes of com.google.walkaround.slob.client.ChannelTestUtil$Package

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.