Package com.goodow.realtime.store.impl

Source Code of com.goodow.realtime.store.impl.ModelImpl

/*
* 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.channel.util.IdGenerator;
import com.goodow.realtime.core.Handler;
import com.goodow.realtime.core.Registration;
import com.goodow.realtime.json.Json;
import com.goodow.realtime.json.JsonArray;
import com.goodow.realtime.json.JsonArray.ListIterator;
import com.goodow.realtime.json.JsonObject;
import com.goodow.realtime.operation.create.CreateComponent;
import com.goodow.realtime.operation.cursor.ReferenceShiftedComponent;
import com.goodow.realtime.operation.list.AbstractListComponent;
import com.goodow.realtime.operation.list.json.JsonInsertComponent;
import com.goodow.realtime.operation.list.string.StringInsertComponent;
import com.goodow.realtime.operation.map.json.JsonMapComponent;
import com.goodow.realtime.store.CollaborativeList;
import com.goodow.realtime.store.CollaborativeMap;
import com.goodow.realtime.store.CollaborativeObject;
import com.goodow.realtime.store.CollaborativeString;
import com.goodow.realtime.store.EventType;
import com.goodow.realtime.store.IndexReference;
import com.goodow.realtime.store.Model;
import com.goodow.realtime.store.UndoRedoStateChangedEvent;

import java.util.logging.Logger;

class ModelImpl implements Model {
  private static final String ROOT_ID = "root";
  private static final Logger log = Logger.getLogger(Model.class.getName());
  /* The mode of the document. If true, the document is readonly. If false it is editable. */
  private boolean isReadOnly;
  boolean canUndo;
  boolean canRedo;
  final JsonObject objects = Json.createObject(); // LinkedHashMap<String, CollaborativeObject>
  private final JsonObject parents = Json.createObject(); // HashMap<String, List<String>>
  private JsonObject indexReferences; // HashMap<String, List<String>>
  final DocumentImpl document;
  final DocumentBridge bridge;
  /* An estimate of the number of bytes used by data stored in the model. */
  double bytesUsed;
  /**
   * The current server version number for this model. The version number begins at 1 (the initial
   * empty model) and is incremented each time the model is changed on the server (either by the
   * current session or any other collaborator). Because this version number includes only changes
   * that the server knows about, it is only updated while this client is connected to the Realtime
   * API server and it does not include changes that have not yet been saved to the server.
   */
  double serverVersion;

  /**
   * @param bridge Internal utilities for the Realtime API.
   * @param document The document that this model belongs to.
   */
  ModelImpl(DocumentBridge bridge, DocumentImpl document) {
    this.bridge = bridge;
    this.document = document;
  }

  @Override
  public Registration onUndoRedoStateChanged(final Handler<UndoRedoStateChangedEvent> handler) {
    return document.addEventListener(null, EventType.UNDO_REDO_STATE_CHANGED, handler, false);
  }

  /**
   * Starts a compound operation. If a name is given, that name will be recorded in the mutation for
   * use in revision history, undo menus, etc. When beginCompoundOperation() is called, all
   * subsequent edits to the data model will be batched together in the undo stack and revision
   * history until endCompoundOperation() is called. Compound operations may be nested inside other
   * compound operations. Note that the compound operation MUST start and end in the same
   * synchronous execution block. If this invariant is violated, the data model will become invalid
   * and all future changes will fail.
   *
   * @param opt_name An optional name for this compound operation.
   */
  public void beginCompoundOperation(String opt_name) {
    log.finer("beginCompoundOperation" + (opt_name == null ? "" : (" " + opt_name)));
  }

  @Override public boolean canRedo() {
    return canRedo;
  }

  @Override public boolean canUndo() {
    return canUndo;
  }

  /**
   * Creates and returns a new collaborative object. This can be used to create custom collaborative
   * objects. For built in types, use the specific create* functions.
   *
   * @param ref An object constructor or type name.
   * @param var_args Arguments to the newly-created object's initialize() method.
   * @return A new collaborative object.
   */
  public Object create(String ref, Object... var_args) {
    throw new UnsupportedOperationException();
  }

  /**
   * Creates the native JS object for a given Brix type.
   *
   * @param ref The type to create.
   * @return The native object.
   */
  public Object createJsObject(String ref) {
    throw new UnsupportedOperationException();
  }

  @Override public CollaborativeList createList(JsonArray opt_initialValue) {
    String id = generateObjectId();
    beginCreationCompoundOperation();
    bridge.consumeAndSubmit(new CreateComponent(id, CreateComponent.LIST));
    if (opt_initialValue != null && opt_initialValue.length() > 0) {
      JsonArray values = JsonSerializer.serializeObjects(opt_initialValue);
      JsonInsertComponent op = new JsonInsertComponent(id, 0, values);
      bridge.consumeAndSubmit(op);
    }
    endCompoundOperation();
    return getObject(id);
  }

  @Override
  public CollaborativeMap createMap(JsonObject opt_initialValue) {
    final String id = generateObjectId();
    beginCreationCompoundOperation();
    bridge.consumeAndSubmit(new CreateComponent(id, CreateComponent.MAP));
    if (opt_initialValue != null && opt_initialValue.size() != 0) {
      opt_initialValue.forEach(new JsonObject.MapIterator<Object>() {
        @Override
        public void call(String key, Object value) {
          JsonArray serializedValue = JsonSerializer.serializeObject(value);
          if (serializedValue == null) {
            return;
          }
          JsonMapComponent op = new JsonMapComponent(id, key, null, serializedValue);
          bridge.consumeAndSubmit(op);
        }
      });
    }
    endCompoundOperation();
    return getObject(id);
  }

  @Override public CollaborativeString createString(String opt_initialValue) {
    String id = generateObjectId();
    beginCreationCompoundOperation();
    bridge.consumeAndSubmit(new CreateComponent(id, CreateComponent.STRING));
    if (opt_initialValue != null && !opt_initialValue.isEmpty()) {
      StringInsertComponent op = new StringInsertComponent(id, 0, opt_initialValue);
      bridge.consumeAndSubmit(op);
    }
    endCompoundOperation();
    return getObject(id);
  }

  /**
   * Ends a compound operation. This method will throw an exception if no compound operation is in
   * progress.
   */
  public void endCompoundOperation() {
    log.finer("endCompoundOperation");
  }

  public <T extends CollaborativeObject> T getObject(String objectId) {
    return objects.<T> get(objectId);
  }

  public JsonArray getParents(String objectId) {
    JsonArray list = parents.getArray(objectId);
    final JsonArray toRtn = Json.createArray();
    if (list != null) {
      list.forEach(new ListIterator<String>() {
        @Override
        public void call(int index, String parent) {
          if (toRtn.indexOf(parent) == -1) {
            toRtn.push(parent);
          }
        }
      });
    }
    return toRtn;
  }

  @Override public CollaborativeMap getRoot() {
    return getObject(ROOT_ID);
  }

  /**
   * Initializes the JS version of a collaborative object.
   *
   * @param obj The JS collaborative object.
   */
  public void initJsObject(Object obj) {
    throw new UnsupportedOperationException();
  }

  /**
   * @return Whether the model is initialized.
   */
  public boolean isInitialized() {
    return false;
  }

  @Override public void redo() {
    bridge.redo();
  }

  @Override public void undo() {
    bridge.undo();
  }

  void addOrRemoveParent(JsonArray childOrNull, String parentId, boolean isAdd) {
    if (childOrNull == null) {
      return;
    }
    if (childOrNull.getNumber(0) == JsonSerializer.REFERENCE_TYPE) {
      String childId = childOrNull.getString(1);
      JsonArray list = parents.getArray(childId);
      if (isAdd) {
        if (list == null) {
          list = Json.createArray();
          parents.set(childId, list);
        }
        list.push(parentId);
      } else {
        assert list != null && list.indexOf(parentId) != -1;
        list.removeValue(parentId);
        if (list.length() == 0) {
          parents.remove(childId);
        }
      }
    }
  }

  IndexReference createIndexReference(String referencedObjectId, int index, boolean canBeDeleted) {
    String id = generateObjectId();
    ReferenceShiftedComponent op =
        new ReferenceShiftedComponent(id, referencedObjectId, index, canBeDeleted, -1);
    beginCreationCompoundOperation();
    bridge.consumeAndSubmit(new CreateComponent(id, CreateComponent.INDEX_REFERENCE));
    bridge.consumeAndSubmit(op);
    registerIndexReference(id, referencedObjectId);
    endCompoundOperation();
    return getObject(id);
  }

  void createRoot() {
    beginCreationCompoundOperation();
    bridge.consumeAndSubmit(new CreateComponent(ROOT_ID, CreateComponent.MAP));
    endCompoundOperation();
  }

  void transformCursor(final AbstractListComponent op, final String userId,
                       final String sessionId) {
    if (indexReferences == null) {
      return;
    }
    JsonArray cursors = indexReferences.getArray(op.id);
    if (cursors != null) {
      cursors.forEach(new ListIterator<String>() {
        @Override
        public void call(int idx, String indexReferenceId) {
          IndexReferenceImpl indexReference = getObject(indexReferenceId);
          int currentIndex = indexReference.index();
          int newIndex = op.transformIndexReference(currentIndex, true,
                                                    indexReference.canBeDeleted());
          if (newIndex != currentIndex) {
            indexReference.consume(userId, sessionId, new ReferenceShiftedComponent(
                indexReferenceId, op.id, newIndex, indexReference.canBeDeleted(), currentIndex));
          }
        }
      });
    }
  }

  /**
   * Starts a compound operation for the creation of the document's initial state.
   */
  private void beginCreationCompoundOperation() {
    beginCompoundOperation("initialize");
  }

  private String generateObjectId() {
    return "gde" + new IdGenerator().next(14);
  }

  private void registerIndexReference(String indexReference, String referencedObject) {
    if (indexReferences == null) {
      indexReferences = Json.createObject();
    }
    JsonArray list = indexReferences.getArray(referencedObject);
    if (list == null) {
      list = Json.createArray();
      indexReferences.set(referencedObject, list);
    }
    list.push(indexReference);
  }
}
TOP

Related Classes of com.goodow.realtime.store.impl.ModelImpl

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.