Package com.goodow.realtime.store.impl

Source Code of com.goodow.realtime.store.impl.DocumentBridge$OutputSink

/*
* Copyright 2012 Goodow.com
*
* 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.goodow.realtime.store.impl;

import com.goodow.realtime.core.Handler;
import com.goodow.realtime.core.Platform;
import com.goodow.realtime.json.Json;
import com.goodow.realtime.json.JsonArray;
import com.goodow.realtime.json.JsonArray.ListIterator;
import com.goodow.realtime.json.JsonElement;
import com.goodow.realtime.json.JsonObject;
import com.goodow.realtime.json.JsonObject.MapIterator;
import com.goodow.realtime.operation.OperationComponent;
import com.goodow.realtime.operation.OperationSink;
import com.goodow.realtime.operation.create.CreateComponent;
import com.goodow.realtime.operation.impl.AbstractComponent;
import com.goodow.realtime.operation.impl.CollaborativeOperation;
import com.goodow.realtime.operation.impl.CollaborativeTransformer;
import com.goodow.realtime.operation.undo.UndoManager;
import com.goodow.realtime.operation.undo.UndoManagerFactory;
import com.goodow.realtime.store.Collaborator;
import com.goodow.realtime.store.Document;
import com.goodow.realtime.store.Error;
import com.goodow.realtime.store.EventType;
import com.goodow.realtime.store.Store;
import com.goodow.realtime.store.UndoRedoStateChangedEvent;
import com.goodow.realtime.store.channel.Constants;
import com.goodow.realtime.store.channel.Constants.Key;

/**
* Internal utilities for the Realtime API.
*/
public class DocumentBridge implements OperationSink<CollaborativeOperation> {
  public interface OutputSink extends OperationSink<CollaborativeOperation> {
    OutputSink VOID = new OutputSink() {
      @Override
      public void close() {
      }

      @Override
      public void consume(CollaborativeOperation op) {
      }
    };

    void close();
  }

  final Store store;
  final String id;
  private final DocumentImpl document;
  private final ModelImpl model;
  private UndoManager<CollaborativeOperation> undoManager = UndoManagerFactory.getNoOp();
  OutputSink outputSink = OutputSink.VOID;

  public DocumentBridge(final Store store, String id, JsonArray components, JsonArray collaborators,
      final Handler<Error> errorHandler) {
    this.store = store == null ? new MemoryStore() : store;
    this.id = id;
    document = new DocumentImpl(this, errorHandler);
    model = document.getModel();

    collaborators = collaborators == null ? Json.createArray() : collaborators;
    collaborators.forEach(new ListIterator<JsonObject>() {
      @Override
      public void call(int index, JsonObject obj) {
        boolean isMe = store.getBus().getSessionId().equals(obj.getString("sessionId"));
        Collaborator collaborator = new CollaboratorImpl(obj.set(Key.IS_ME, isMe));
        document.collaborators.set(collaborator.sessionId(), collaborator);
      }
    });

    if (components != null && components.length() > 0) {
      final CollaborativeTransformer transformer = new CollaborativeTransformer();
      CollaborativeOperation operation =
          transformer.createOperation(Json.createObject().set("op", components));
      applyLocally(operation);
    }
  }

  /*
   * Incoming operations from remote
   */
  @Override
  public void consume(CollaborativeOperation operation) {
    applyLocally(operation);
    undoManager.nonUndoableOp(operation);
  }

  public void createRoot() {
    model.createRoot();
  }

  public Document getDocument() {
    return document;
  }

  public <T> void scheduleHandle(final Handler<T> handler, final T event) {
    Platform.scheduler().scheduleDeferred(new Handler<Void>() {
      @Override
      public void handle(Void ignore) {
        Platform.scheduler().handle(handler, event);
      }
    });
  }

  public void setOutputSink(OutputSink outputSink) {
    this.outputSink = outputSink;
  }

  public void setUndoEnabled(boolean undoEnabled) {
    undoManager =
        undoEnabled ? UndoManagerFactory.createUndoManager() : UndoManagerFactory
            .<CollaborativeOperation> getNoOp();
  }

  public JsonObject toJson() {
    return model.getRoot().toJson();
  }

  public JsonArray toSnapshot() {
    final JsonArray createComponents = Json.createArray();
    final JsonArray components = Json.createArray();
    model.objects.forEach(new MapIterator<CollaborativeObjectImpl>() {
      @Override
      public void call(String key, CollaborativeObjectImpl object) {
        OperationComponent<?>[] initializeComponents = object.toInitialization();
        boolean isCreateOp = true;
        for (OperationComponent<?> component : initializeComponents) {
          if (isCreateOp) {
            createComponents.push(component.toJson());
            isCreateOp = false;
          } else {
            components.push(component.toJson());
          }
        }
      }
    });
    components.forEach(new ListIterator<JsonElement>() {
      @Override
      public void call(int index, JsonElement component) {
        createComponents.push(component);
      }
    });
    return createComponents;
  }

  @Override
  public String toString() {
    return toJson().toJsonString();
  }

  void consumeAndSubmit(OperationComponent<?> component) {
    Collaborator me = document.collaborators.get(store.getBus().getSessionId());
    CollaborativeOperation operation =
        new CollaborativeOperation(me == null ? null : me.userId(), store.getBus().getSessionId(),
                                   Json.createArray().push(component));
    applyLocally(operation);
    undoManager.checkpoint();
    undoManager.undoableOp(operation);
    mayUndoRedoStateChanged();
    outputSink.consume(operation);
  }

  boolean isLocalSession(String sessionId) {
    return store.getBus().getSessionId().equals(sessionId);
  }

  void redo() {
    bypassUndoStack(undoManager.redo());
  }

  void undo() {
    bypassUndoStack(undoManager.undo());
  }

  private void applyLocally(final CollaborativeOperation operation) {
    operation.components.forEach(new ListIterator<AbstractComponent<?>>() {
      @Override
      public void call(int index, AbstractComponent<?> component) {
        if (component.type != CreateComponent.TYPE) {
          model.<CollaborativeObjectImpl>getObject(component.id).consume(operation.userId, operation.sessionId, component);
          return;
        }
        CollaborativeObjectImpl obj;
        switch (((CreateComponent) component).subType) {
          case CreateComponent.MAP:
            obj = new CollaborativeMapImpl(model);
            break;
          case CreateComponent.LIST:
            obj = new CollaborativeListImpl(model);
            break;
          case CreateComponent.STRING:
            obj = new CollaborativeStringImpl(model);
            break;
          case CreateComponent.INDEX_REFERENCE:
            obj = new IndexReferenceImpl(model);
            break;
          default:
            throw new RuntimeException("Shouldn't reach here!");
        }
        obj.id = component.id;
        model.objects.set(obj.id, obj);
        model.bytesUsed += component.toString().length();
        model.bytesUsed++;
      }
    });
  }

  /**
   * Applies an op locally and send it bypassing the undo stack. This is necessary with operations
   * popped from the undoManager as they are automatically applied.
   *
   * @param operation
   */
  private void bypassUndoStack(CollaborativeOperation operation) {
    applyLocally(operation);
    outputSink.consume(operation);
    mayUndoRedoStateChanged();
  }

  private void mayUndoRedoStateChanged() {
    boolean canUndo = undoManager.canUndo();
    boolean canRedo = undoManager.canRedo();
    if (model.canUndo() != canUndo || model.canRedo() != canRedo) {
      model.canUndo = canUndo;
      model.canRedo = canRedo;
      UndoRedoStateChangedEvent event =
          new UndoRedoStateChangedEventImpl(model, Json.createObject().set("canUndo", canUndo)
              .set("canRedo", canRedo));
      store.getBus().publishLocal(Constants.Topic.STORE + "/" + id + "/" + EventType.UNDO_REDO_STATE_CHANGED,
                                  event);
    }
  }
}
TOP

Related Classes of com.goodow.realtime.store.impl.DocumentBridge$OutputSink

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.