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

com.goodow.realtime.CollaborativeString 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.ModelFactory;
import com.goodow.realtime.model.util.ModelNative;
import com.goodow.realtime.operation.Operation;
import com.goodow.realtime.operation.create.CreateOperation;
import com.goodow.realtime.operation.list.ListTarget;
import com.goodow.realtime.operation.list.string.StringDeleteOperation;
import com.goodow.realtime.operation.list.string.StringInsertOperation;

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 java.util.Set;

/**
 * Creates a new collaborative string. Unlike regular JavaScript strings, collaborative strings are
 * mutable.
 * 

* Changes to the string will automatically be synced with the server and other collaborators. To * listen for changes, add EventListeners for the following event types: *

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

* This class should not be instantiated directly. To create a new collaborative string, use * {@link com.goodow.realtime.Model#createString(String)} */ @ExportPackage(ModelFactory.PACKAGE_PREFIX_REALTIME) @Export(all = true) public class CollaborativeString extends CollaborativeObject { @GwtIncompatible(ModelFactory.JS_REGISTER_PROPERTIES) @ExportAfterCreateMethod // @formatter:off public native static void __jsniRunAfter__() /*-{ // var _ = $wnd.good.realtime.CollaborativeString.prototype; // Object.defineProperties(_, { // id : { // get : function() { // return [email protected]::id; // } // }, // length : { // get : function() { // return [email protected]::length()(); // } // } // }); }-*/; // @formatter:on private final StringBuilder snapshot; CollaborativeString(Model model) { super(model); snapshot = new StringBuilder(); } public void addTextDeletedListener(EventHandler handler) { addEventListener(EventType.TEXT_DELETED, handler, false); } public void addTextInsertedListener(EventHandler handler) { addEventListener(EventType.TEXT_INSERTED, handler, false); } /** * Appends a string to the end of this one. * * @param text The new text to append. * @exception java.lang.IllegalArgumentException */ public void append(String text) { insertString(length(), text); } /** * Gets a string representation of the collaborative string. * * @return A string representation of the collaborative string. */ public String getText() { return snapshot.toString(); } /** * Inserts a string into the collaborative string at a specific index. * * @param index The index to insert at. * @param text The new text to insert. * @exception java.lang.IllegalArgumentException * @exception java.lang.StringIndexOutOfBoundsException */ public void insertString(int index, String text) { if (text == null || text.isEmpty()) { throw new IllegalArgumentException( "At least one value must be specified for an insert mutation. text: " + text); } checkIndex(index); StringInsertOperation op = new StringInsertOperation(id, index, text); consumeAndSubmit(op); } /** * @return The length of the string. Read only. */ public int length() { return snapshot.length(); } /** * Creates an IndexReference at the given {@code index}. If {@code canBeDeleted} is set, then a * delete over the index will delete the reference. Otherwise the reference will shift to the * beginning of the deleted range. * * @param index The index of the reference. * @param canBeDeleted Whether this index is deleted when there is a delete of a range covering * this index. * @return The newly registered reference. */ public IndexReference registerReference(int index, boolean canBeDeleted) { checkIndex(index); return model.createIndexReference(id, index, canBeDeleted); } /** * Deletes the text between startIndex (inclusive) and endIndex (exclusive). * * @param startIndex The start index of the range to delete (inclusive). * @param endIndex The end index of the range to delete (exclusive). * @exception java.lang.StringIndexOutOfBoundsException */ public void removeRange(int startIndex, int endIndex) { int length = length(); if (startIndex < 0 || startIndex >= length || endIndex <= startIndex || endIndex > length) { throw new StringIndexOutOfBoundsException("StartIndex: " + startIndex + ", EndIndex: " + endIndex + ", Size: " + length); } StringDeleteOperation op = new StringDeleteOperation(id, startIndex, snapshot.substring(startIndex, endIndex)); consumeAndSubmit(op); } public void removeStringListener(EventHandler handler) { removeEventListener(EventType.TEXT_INSERTED, handler, false); removeEventListener(EventType.TEXT_DELETED, handler, false); } /** * Sets the contents of this collaborative string. Note that this method performs a text diff * between the current string contents and the new contents so that the string will be modified * using the minimum number of text inserts and deletes possible to change the current contents to * the newly-specified contents. * * @param text The new value of the string. */ public void setText(String text) { if (text == null) { throw new IllegalArgumentException("Expected string for text, but was: null"); } model.beginCompoundOperation("replaceText"); ModelNative.get().setText(this, text); model.endCompoundOperation(); } @SuppressWarnings("unchecked") @Override protected void consume(final String userId, final String sessionId, Operation operation) { ((Operation>) operation).apply(new ListTarget() { @Override public void delete(int startIndex, int length) { deleteAndFireEvent(startIndex, length, sessionId, userId); } @Override public void insert(int startIndex, String values) { insertAndFireEvent(startIndex, values, sessionId, userId); } @Override public void replace(int startIndex, String values) { throw new UnsupportedOperationException(); } }); } @Override Operation[] toInitialization() { Operation[] toRtn = new Operation[1 + (length() == 0 ? 0 : 1)]; toRtn[0] = new CreateOperation(id, CreateOperation.STRING); if (length() != 0) { toRtn[1] = new StringInsertOperation(id, 0, getText()); } return toRtn; } @Override void toString(Set seen, StringBuilder sb) { if (seen.contains(id)) { sb.append(""); return; } seen.add(id); sb.append(getText()); } private void checkIndex(int index) { int length = length(); if (index < 0 || index > length) { throw new StringIndexOutOfBoundsException("Index: " + index + ", Size: " + length); } } private void deleteAndFireEvent(int startIndex, int length, String sessionId, String userId) { int endIndex = startIndex + length; assert length > 0 && endIndex <= length(); String toDelete = snapshot.substring(startIndex, endIndex); TextDeletedEvent event = new TextDeletedEvent(this, sessionId, userId, startIndex, toDelete); snapshot.delete(startIndex, endIndex); fireEvent(event); model.setIndexReferenceIndex(id, false, startIndex, length, sessionId, userId); model.bytesUsed -= length; } private void insertAndFireEvent(int index, String text, String sessionId, String userId) { assert index <= length(); TextInsertedEvent event = new TextInsertedEvent(this, sessionId, userId, index, text); snapshot.insert(index, text); fireEvent(event); model.setIndexReferenceIndex(id, true, index, text.length(), sessionId, userId); model.bytesUsed += text.length(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy