All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.goodow.realtime.Model Maven / Gradle / Ivy

/*
 * 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;

import com.goodow.realtime.model.util.JsonSerializer;
import com.goodow.realtime.model.util.ModelFactory;
import com.goodow.realtime.operation.create.CreateOperation;
import com.goodow.realtime.operation.cursor.ReferenceShiftedOperation;
import com.goodow.realtime.operation.id.IdGenerator;
import com.goodow.realtime.operation.list.json.JsonInsertOperation;
import com.goodow.realtime.operation.list.string.StringInsertOperation;
import com.goodow.realtime.operation.map.json.JsonMapOperation;
import com.goodow.realtime.operation.util.JsonUtility;

import com.google.common.annotations.GwtIncompatible;

import org.timepedia.exporter.client.Export;
import org.timepedia.exporter.client.ExportAfterCreateMethod;
import org.timepedia.exporter.client.ExportPackage;
import org.timepedia.exporter.client.NoExport;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Logger;

import elemental.json.JsonArray;
import elemental.json.JsonValue;
import elemental.util.ArrayOfString;
import elemental.util.MapFromStringTo;

/**
 * The collaborative model is the data model for a Realtime document. The document's object graph
 * should be added to the model under the root object. All objects that are part of the model must
 * be accessible from this root.
 * 

* The model class is also used to create instances of built in and custom collaborative objects via * the appropriate create method. *

* Listen on the model for the following events: *

    *
  • {@link com.goodow.realtime.EventType#UNDO_REDO_STATE_CHANGED} *
*

* This class should not be instantiated directly. The collaborative model is generated during the * document load process. The model can be initialized by passing an initializer function to * com.goodow.realtime.load. */ @ExportPackage(ModelFactory.PACKAGE_PREFIX_REALTIME) @Export(all = true) public class Model implements EventTarget { static final String EVENT_HANDLER_KEY = "model"; private static final String ROOT_ID = "root"; private static final Logger log = Logger.getLogger(Model.class.getName()); @GwtIncompatible(ModelFactory.JS_REGISTER_PROPERTIES) @ExportAfterCreateMethod // @formatter:off public native static void __jsniRunAfter__() /*-{ var _ = $wnd.good.realtime.Model.prototype; // Object.defineProperties(_, { // canRedo : { // get : function() { // return [email protected]::canRedo()(); // } // }, // canUndo : { // get : function() { // return [email protected]::canUndo()(); // } // }, // isReadOnly : { // get : function() { // return [email protected]::isReadOnly()(); // } // } // }); _.createMap = function(opt_initialValue) { var jsMap; if (opt_initialValue !== undefined) { jsMap = {}; for ( var key in opt_initialValue) { if (Object.prototype.hasOwnProperty.call(opt_initialValue, key) && key != '$H') { jsMap[key] = @org.timepedia.exporter.client.ExporterUtil::gwtInstance(Ljava/lang/Object;)(opt_initialValue[key]); } } } var v = [email protected]::__jsniCreateMap__(Lelemental/util/MapFromStringTo;)(jsMap); return @org.timepedia.exporter.client.ExporterUtil::wrap(Ljava/lang/Object;)(v); }; _.getObject = function(objectId) { var v = [email protected]::getObject(Ljava/lang/String;)(objectId); return @org.timepedia.exporter.client.ExporterUtil::wrap(Ljava/lang/Object;)(v); }; }-*/; // @formatter:on private boolean isReadOnly; boolean canUndo; boolean canRedo; final Map objects = new LinkedHashMap(); private final Map> parents = new HashMap>(); private Map> indexReferences; final Document document; final DocumentBridge bridge; double bytesUsed; /** * @param bridge The driver for the GWT collaborative libraries. * @param document The document that this model belongs to. */ Model(DocumentBridge bridge, Document document) { this.bridge = bridge; this.document = document; } @Override public void addEventListener(EventType type, EventHandler handler, boolean opt_capture) { document.addEventListener(EVENT_HANDLER_KEY, type, handler, opt_capture); } public void addUndoRedoStateChangedListener(EventHandler handler) { addEventListener(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.info("beginCompoundOperation" + (opt_name == null ? "" : (" " + opt_name))); } /** * @return True if the model can currently redo. */ public boolean canRedo() { return canRedo; } /** * @return True if the model can currently undo. */ 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 a collaborative list. * * @param opt_initialValue Initial value for the list. * @return A collaborative list. */ public CollaborativeList createList(Object... opt_initialValue) { String id = generateObjectId(); beginCreationCompoundOperation(); bridge.consumeAndSubmit(new CreateOperation(id, CreateOperation.LIST)); if (opt_initialValue != null && opt_initialValue.length > 0) { JsonValue[] values = JsonSerializer.serializeObjects(opt_initialValue); JsonInsertOperation op = new JsonInsertOperation(id, 0, values); bridge.consumeAndSubmit(op); } endCompoundOperation(); return getObject(id); } /** * Creates a collaborative map. * * @param opt_initialValue Initial value for the map. * @return A collaborative map. */ @NoExport public CollaborativeMap createMap(Map opt_initialValue) { String id = generateObjectId(); beginCreationCompoundOperation(); bridge.consumeAndSubmit(new CreateOperation(id, CreateOperation.MAP)); if (opt_initialValue != null && !opt_initialValue.isEmpty()) { for (Map.Entry entry : opt_initialValue.entrySet()) { JsonArray serializedValue = JsonSerializer.serializeObject(entry.getValue()); if (serializedValue == null) { continue; } JsonMapOperation op = new JsonMapOperation(id, entry.getKey(), null, serializedValue); bridge.consumeAndSubmit(op); } } endCompoundOperation(); return getObject(id); } /** * Creates a collaborative string. * * @param opt_initialValue Sets the initial value for this string. * @return A collaborative string. */ public CollaborativeString createString(String opt_initialValue) { String id = generateObjectId(); beginCreationCompoundOperation(); bridge.consumeAndSubmit(new CreateOperation(id, CreateOperation.STRING)); if (opt_initialValue != null && !opt_initialValue.isEmpty()) { StringInsertOperation op = new StringInsertOperation(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.info("endCompoundOperation"); } public double getBytesUsed() { return bytesUsed; } @SuppressWarnings("unchecked") @NoExport public T getObject(String objectId) { return (T) objects.get(objectId); } // @Export("extra.getParents") public String[] getParents(String objectId) { List list = parents.get(objectId); if (list == null) { return null; } Set set = new HashSet(list); return set.toArray(new String[0]); } /** * Returns the root of the object model. * * @return The root of the object model. */ public CollaborativeMap getRoot() { return getObject(ROOT_ID); } /** * Returns whether the model is initialized. * * @return Whether the model is initialized. */ public boolean isInitialized() { return false; } /** * @return The mode of the document. If true, the document is readonly. If false it is editable. */ public boolean isReadOnly() { return isReadOnly; } /** * Redo the last thing the active collaborator undid. */ public void redo() { bridge.redo(); } @Override public void removeEventListener(EventType type, EventHandler handler, boolean opt_capture) { document.removeEventListener(EVENT_HANDLER_KEY, type, handler, opt_capture); } /** * Undo the last thing the active collaborator did. */ public void undo() { bridge.undo(); } void addOrRemoveParent(JsonValue childOrNull, String parentId, boolean isAdd) { if (JsonUtility.isNull(childOrNull)) { return; } JsonArray child = (JsonArray) childOrNull; if (child.getNumber(0) == JsonSerializer.REFERENCE_TYPE) { String childId = child.getString(1); List list = parents.get(childId); if (isAdd) { if (list == null) { list = new ArrayList(); parents.put(childId, list); } list.add(parentId); } else { assert list != null && list.contains(parentId); list.remove(parentId); if (list.isEmpty()) { parents.remove(childId); } } } } IndexReference createIndexReference(String referencedObjectId, int index, boolean canBeDeleted) { String id = generateObjectId(); ReferenceShiftedOperation op = new ReferenceShiftedOperation(id, referencedObjectId, index, canBeDeleted, -1); beginCreationCompoundOperation(); bridge.consumeAndSubmit(new CreateOperation(id, CreateOperation.INDEX_REFERENCE)); bridge.consumeAndSubmit(op); registerIndexReference(id, referencedObjectId); endCompoundOperation(); return getObject(id); } void createRoot() { beginCreationCompoundOperation(); bridge.consumeAndSubmit(new CreateOperation(ROOT_ID, CreateOperation.MAP)); endCompoundOperation(); } void setIndexReferenceIndex(String referencedObject, boolean isInsert, int index, int length, String sessionId, String userId) { if (indexReferences == null) { return; } List list = indexReferences.get(referencedObject); if (list != null) { for (String indexReferenceId : list) { IndexReference indexReference = getObject(indexReferenceId); indexReference.setIndex(isInsert, index, length, sessionId, userId); } } } @GwtIncompatible(ModelFactory.JS_REGISTER_MATHODS) private CollaborativeMap __jsniCreateMap__(MapFromStringTo map) { if (map == null) { return createMap(null); } HashMap opt_initialValue = new HashMap(); ArrayOfString keys = map.keys(); for (int i = 0, len = keys.length(); i < len; i++) { String key = keys.get(i); opt_initialValue.put(key, map.get(key)); } return createMap(opt_initialValue); } /** * 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 = new HashMap>(); } List list = indexReferences.get(referencedObject); if (list == null) { list = new ArrayList(); indexReferences.put(referencedObject, list); } list.add(indexReference); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy