100% documentation

This commit is contained in:
Kevin Jahns
2014-08-09 00:31:30 +02:00
parent baf4660733
commit b340bbe806
73 changed files with 1030 additions and 438 deletions

View File

@@ -1,4 +1,7 @@
#
# @param {Function} callback The callback is called when the connector is initialized.
#
createIwcConnector = (callback)->
iwcHandler = {}
duiClient = new DUIClient()
@@ -21,6 +24,13 @@ createIwcConnector = (callback)->
# @see http://dbis.rwth-aachen.de/cms/projects/ROLE
#
class IwcConnector
#
# @param {Engine} engine The transformation engine
# @param {HistoryBuffer} HB
# @param {Array<Function>} execution_listener You must ensure that whenever an operation is executed, every function in this Array is called.
# @param {Yatta} yatta The Yatta framework.
#
constructor: (@engine, @HB, @execution_listener, @yatta)->
@duiClient = duiClient
@iwcHandler = iwcHandler
@@ -44,14 +54,27 @@ createIwcConnector = (callback)->
@sendIwcIntent "Yatta_push_HB_element", json
@iwcHandler["Yatta_get_HB_element"] = [sendHistoryBuffer]
#
# This function is called whenever an operation was executed.
# @param {Operation} o The operation that was executed.
#
send: (o)->
if o.uid.creator is @HB.getUserId() and (typeof o.uid.op_number isnt "string")
@sendIwcIntent "Yatta_new_operation", o
#
# This function is called whenever an operation was received from another peer.
# @param {Operation} o The operation that was received.
#
receive: (o)->
if o.uid.creator isnt @HB.getUserId()
@engine.applyOp o
#
# Helper for sending iwc intents.
# @param {String} action_name The name of the action that is going to be send.
# @param {String} content The content that is atteched to the intent.
#
sendIwcIntent: (action_name, content)->
intent =
action: action_name
@@ -62,9 +85,6 @@ createIwcConnector = (callback)->
@duiClient.sendIntent(intent)
sync: ()->
throw new Error "Can't use this a.t.m."
get_HB_intent =
action: "Yatta_get_HB_element"
component: ""

View File

@@ -7,6 +7,13 @@ module.exports = (user_list)->
# A trivial Connector that simulates network delay.
#
class TestConnector
#
# @param {Engine} engine The transformation engine
# @param {HistoryBuffer} HB
# @param {Array<Function>} execution_listener You must ensure that whenever an operation is executed, every function in this Array is called.
# @param {Yatta} yatta The Yatta framework.
#
constructor: (@engine, @HB, @execution_listener)->
send_ = (o)=>
@send o
@@ -21,29 +28,49 @@ module.exports = (user_list)->
@unexecuted = {}
#
# This engine applied operations in a specific order.
# Get the ops in the right order.
#
getOpsInExecutionOrder: ()->
@applied_operations
#
# This function is called whenever an operation was executed.
# @param {Operation} o The operation that was executed.
#
send: (o)->
if (o.uid.creator is @HB.getUserId()) and (typeof o.uid.op_number isnt "string")
for user in user_list
if user.getUserId() isnt @HB.getUserId()
user.getConnector().receive(o)
#
# This function is called whenever an operation was received from another peer.
# @param {Operation} o The operation that was received.
#
receive: (o)->
@unexecuted[o.uid.creator] ?= []
@unexecuted[o.uid.creator].push o
#
# Flush one operation from the line of a specific user.
#
flushOne: (user)->
if @unexecuted[user]?.length > 0
@engine.applyOp @unexecuted[user].shift()
#
# Flush one operation on a random line.
#
flushOneRandom: ()->
@flushOne (_.random 0, (user_list.length-1))
#
# Flush all operations on every line.
#
flushAll: ()->
for n,ops of @unexecuted
@engine.applyOps ops
@unexecuted = {}
sync: ()->
throw new Error "Can't use this a.t.m."

View File

@@ -3,6 +3,11 @@
# The Engine handles how and in which order to execute operations and add operations to the HistoryBuffer.
#
class Engine
#
# @param {HistoryBuffer} HB
# @param {Array} parser Defines how to parse encoded messages.
#
constructor: (@HB, @parser)->
@unprocessed_ops = []
@@ -31,6 +36,10 @@ class Engine
@unprocessed_ops.push o
@tryUnprocessed()
#
# Same as applyOps but operations that are already in the HB are not applied.
# @see Engine.applyOps
#
applyOpsCheckDouble: (ops_json)->
for o in ops_json
if @HB.getOperation(o.uid)?
@@ -49,6 +58,7 @@ class Engine
applyOp: (op_json)->
# $parse_and_execute will return false if $o_json was parsed and executed, otherwise the parsed operadion
o = @parseOperation op_json
@HB.addToCounter o
# @HB.addOperation o
if not o.execute()
@unprocessed_ops.push o

View File

@@ -11,6 +11,11 @@ Engine = require "../Engine.coffee"
# * Array
#
class JsonYatta
#
# @param {String} user_id Unique id of the peer.
# @param {Connector} Connector the connector class.
#
constructor: (user_id, Connector)->
@HB = new HistoryBuffer user_id
json_types = json_types_uninitialized @HB
@@ -21,7 +26,6 @@ class JsonYatta
@HB.addOperation(first_word).execute()
@root_element = first_word
#
# @result JsonType
#

View File

@@ -7,46 +7,76 @@ Engine = require "../Engine.coffee"
# Framework for Text Datastructures.
#
class TextYatta
#
# @param {String} user_id Uniqe user id that defines this peer.
# @param {Connector} Connector The connector defines how you connect to the other peers.
#
constructor: (user_id, Connector)->
@HB = new HistoryBuffer user_id
text_types = text_types_uninitialized @HB
@engine = new Engine @HB, text_types.parser
@connector = new Connector @engine, @HB, text_types.execution_listener
root_elem = @connector.getRootElement()
if not root_elem?
first_word = new text_types.types.Word @HB.getNextOperationIdentifier()
@HB.addOperation(first_word)
first_word.execute()
@root_element = @HB.addOperation(new text_types.types.ReplaceManager first_word, @HB.getNextOperationIdentifier()).execute()
else
@root_element = @HB.getOperation(root_elem)
first_word = new text_types.types.Word undefined
@HB.addOperation(first_word).execute()
@root_element = first_word
#
# @result Word
#
getRootElement: ()->
@root_element
#
# @see Engine
#
getEngine: ()->
@engine
#
# Get the initialized connector.
#
getConnector: ()->
@connector
#
# @see HistoryBuffer
#
getHistoryBuffer: ()->
@HB
#
# Get the UserId from the HistoryBuffer object.
# In most cases this will be the same as the user_id value with which
# JsonYatta was initialized (Depending on the HistoryBuffer implementation).
#
getUserId: ()->
@HB.getUserId()
#
# @see JsonType.val
#
val: ()->
@root_element.val().val()
@root_element.val()
#
# @see Word.insertText
#
insertText: (pos, content)->
@root_element.val().insertText pos, content
@root_element.insertText pos, content
#
# @see Word.deleteText
#
deleteText: (pos, length)->
@root_element.val().deleteText pos, length
@root_element.deleteText pos, length
#
# @see Word.replaceText
#
replaceText: (text)->
@root_element.val().replaceText text
@root_element.replaceText text
module.exports = TextYatta

View File

@@ -42,6 +42,9 @@ class HistoryBuffer
res[user] = ctn
res
#
# Encode this operation in such a way that it can be parsed by remote peers.
#
_encode: (state_vector={})->
json = []
unknown = (user, o_number)->
@@ -84,28 +87,38 @@ class HistoryBuffer
@operation_counter[user_id]++
uid
#
# Retrieve an operation from a unique id.
#
getOperation: (uid)->
if uid instanceof Object
@buffer[uid.creator]?[uid.op_number]
else if not uid?
else
throw new Error "This type of uid is not defined!"
#
# Add an operation to the HB. Note that this will not link it against
# other operations (it wont executed)
#
addOperation: (o)->
if not @buffer[o.creator]?
@buffer[o.creator] = {}
if not @operation_counter[o.creator]?
@operation_counter[o.creator] = 0
#if @operation_counter[o.creator] isnt o.op_number and typeof o.op_number is 'number'
# throw new Error "You don't receive operations in the proper order. Try counting like this 0,1,2,3,4,.. ;)"
if @buffer[o.creator][o.op_number]?
throw new Error "You must not overwrite operations!"
@buffer[o.creator][o.op_number] = o
if typeof o.op_number is 'number' and o.creator isnt @getUserId()
@operation_counter[o.creator]++
o
#
# Increment the operation_counter that defines the current state of the Engine.
#
addToCounter: (o)->
if not @operation_counter[o.creator]?
@operation_counter[o.creator] = 0
if typeof o.op_number is 'number' and o.creator isnt @getUserId()
@operation_counter[o.creator]++
#if @operation_counter[o.creator] isnt (o.op_number + 1)
#console.log (@operation_counter[o.creator] - (o.op_number + 1))
#console.log o
#throw new Error "You don't receive operations in the proper order. Try counting like this 0,1,2,3,4,.. ;)"
module.exports = HistoryBuffer

View File

@@ -303,6 +303,9 @@ module.exports = (HB)->
val : ()->
@content
#
# Encode this operation in such a way that it can be parsed by remote peers.
#
_encode: ()->
json = {
'type': "ImmutableObject"

View File

@@ -64,6 +64,10 @@ module.exports = (HB)->
# console.log(w.newProperty == "Awesome") # true!
#
class JsonWrapper
#
# @param {JsonType} jsonType Instance of the JsonType that this class wrappes.
#
constructor: (jsonType)->
for name, obj of jsonType.map
do (name, obj)->
@@ -105,9 +109,15 @@ module.exports = (HB)->
for name,o of initial_value
@val name, o, mutable
#
# Whether the default is 'mutable' (true) or 'immutable' (false)
#
mutable_default:
true
#
# Set if the default is 'mutable' or 'immutable'
# @param {String|Boolean} mutable Set either 'mutable' / true or 'immutable' / false
setMutableDefault: (mutable)->
if mutable is true or mutable is 'mutable'
JsonType.prototype.mutable_default = true

View File

@@ -17,6 +17,9 @@ module.exports = (HB)->
@map = {}
super uid
#
# @see JsonTypes.val
#
val: (name, content)->
if content?
if not @map[name]?
@@ -55,6 +58,12 @@ module.exports = (HB)->
@saveOperation 'map_manager', map_manager
super uid
#
# If map_manager doesn't have the property name, then add it.
# The ReplaceManager that is being written on the property is unique
# in such a way that if AddName is executed (from another peer) it will
# always have the same result (ReplaceManager, and its beginning and end are the same)
#
execute: ()->
if not @validateSavedOperations()
return false
@@ -72,6 +81,9 @@ module.exports = (HB)->
@map_manager.map[@name] = HB.addOperation(new ReplaceManager undefined, uid_r, beg, end).execute()
super
#
# Encode this operation in such a way that it can be parsed by remote peers.
#
_encode: ()->
{
'type' : "AddName"
@@ -163,17 +175,27 @@ module.exports = (HB)->
if initial_content?
@replace initial_content
#
# Replace the existing word with a new word.
#
replace: (content)->
o = @getLastOperation()
op = new Replaceable content, @, undefined, o, o.next_cl
HB.addOperation(op).execute()
#
# Get the value of this Word
# @return {String}
#
val: ()->
o = @getLastOperation()
if o instanceof types.Delimiter
throw new Error "dtrn"
o.val()
#
# Encode this operation in such a way that it can be parsed by remote peers.
#
_encode: ()->
json =
{
@@ -220,12 +242,22 @@ module.exports = (HB)->
throw new Error "You must define content, prev, and next for Replaceable-types!"
super uid, prev, next, origin
#
# Return the content that this operation holds.
#
val: ()->
@content
#
# Replace the content of this replaceable with new content.
#
replace: (content)->
@parent.replace content
#
# If possible set the replace manager in the content.
# @see Word.setReplaceManager
#
execute: ()->
if not @validateSavedOperations()
return false
@@ -235,8 +267,7 @@ module.exports = (HB)->
@
#
# Convert all relevant information of this operation to the json-format.
# This result can be send to other clients.
# Encode this operation in such a way that it can be parsed by remote peers.
#
_encode: ()->
json =

View File

@@ -140,6 +140,9 @@ module.exports = (HB)->
@saveOperation 'replace_manager', op
@validateSavedOperations
#
# Encode this operation in such a way that it can be parsed by remote peers.
#
_encode: ()->
json = {
'type': "Word"