refs, complex saveOperation' and validateSavedOperations`

This commit is contained in:
Kevin Jahns 2015-04-17 20:11:05 +02:00
parent f44f463e9d
commit b02662c36e
14 changed files with 4396 additions and 1008 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -154,32 +154,67 @@ module.exports = function() {
return json;
};
Operation.prototype.saveOperation = function(name, op) {
Operation.prototype.saveOperation = function(name, op, base) {
var base1, dest, j, last_path, len, path, paths;
if (base == null) {
base = "this";
}
if (op == null) {
} else if ((op.execute != null) || !((op.op_number != null) && (op.creator != null))) {
return this[name] = op;
if (base === "this") {
return this[name] = op;
} else {
dest = this[base];
paths = name.split("/");
last_path = paths.pop();
for (j = 0, len = paths.length; j < len; j++) {
path = paths[j];
dest = dest[path];
}
return dest[last_path] = op;
}
} else {
if (this.unchecked == null) {
this.unchecked = {};
}
return this.unchecked[name] = op;
if ((base1 = this.unchecked)[base] == null) {
base1[base] = {};
}
return this.unchecked[base][name] = op;
}
};
Operation.prototype.validateSavedOperations = function() {
var name, op, op_uid, ref, success, uninstantiated;
var base, base_name, dest, j, last_path, len, name, op, op_uid, path, paths, ref, success, uninstantiated;
uninstantiated = {};
success = this;
ref = this.unchecked;
for (name in ref) {
op_uid = ref[name];
op = this.HB.getOperation(op_uid);
if (op) {
this[name] = op;
} else {
uninstantiated[name] = op_uid;
success = false;
for (base_name in ref) {
base = ref[base_name];
for (name in base) {
op_uid = base[name];
op = this.HB.getOperation(op_uid);
if (op) {
if (base_name === "this") {
this[name] = op;
} else {
dest = this[base_name];
paths = name.split("/");
last_path = paths.pop();
for (j = 0, len = paths.length; j < len; j++) {
path = paths[j];
dest = dest[path];
}
dest[last_path] = op;
}
} else {
if (uninstantiated[base_name] == null) {
uninstantiated[base_name] = {};
}
uninstantiated[base_name][name] = op_uid;
success = false;
}
}
}
delete this.unchecked;
@ -253,7 +288,8 @@ module.exports = function() {
ops.Insert = (function(superClass) {
extend(Insert, superClass);
function Insert(custom_type, content, parent, uid, prev_cl, next_cl, origin) {
function Insert(custom_type, content, content_operations, parent, uid, prev_cl, next_cl, origin) {
var name, op;
if (content === void 0) {
} else if ((content != null) && (content.creator != null)) {
@ -261,6 +297,13 @@ module.exports = function() {
} else {
this.content = content;
}
if (content_operations != null) {
this.content_operations = {};
for (name in content_operations) {
op = content_operations[name];
this.saveOperation(name, op, 'content_operations');
}
}
this.saveOperation('parent', parent);
this.saveOperation('prev_cl', prev_cl);
this.saveOperation('next_cl', next_cl);
@ -275,8 +318,28 @@ module.exports = function() {
Insert.prototype.type = "Insert";
Insert.prototype.val = function() {
if ((this.content != null) && (this.content.getCustomType != null)) {
return this.content.getCustomType();
var content, n, ref, ref1, v;
if (this.content != null) {
if (this.content.getCustomType != null) {
return this.content.getCustomType();
} else if (this.content.constructor === Object) {
content = {};
ref = this.content;
for (n in ref) {
v = ref[n];
content[n] = v;
}
if (this.content_operations != null) {
ref1 = this.content_operations;
for (n in ref1) {
v = ref1[n];
content[n] = v;
}
}
return content;
} else {
return this.content;
}
} else {
return this.content;
}
@ -380,14 +443,14 @@ module.exports = function() {
};
Insert.prototype.execute = function() {
var base, distance_to_origin, i, o;
var base1, distance_to_origin, i, o;
if (!this.validateSavedOperations()) {
return false;
} else {
if (this.content instanceof ops.Operation) {
this.content.insert_parent = this;
if ((base = this.content).referenced_by == null) {
base.referenced_by = 0;
if ((base1 = this.content).referenced_by == null) {
base1.referenced_by = 0;
}
this.content.referenced_by++;
}
@ -461,7 +524,7 @@ module.exports = function() {
};
Insert.prototype._encode = function(json) {
var ref;
var n, o, operations, ref, ref1;
if (json == null) {
json = {};
}
@ -474,9 +537,18 @@ module.exports = function() {
}
json.parent = this.parent.getUid();
if (((ref = this.content) != null ? ref.getUid : void 0) != null) {
json['content'] = this.content.getUid();
json.content = this.content.getUid();
} else {
json['content'] = JSON.stringify(this.content);
json.content = JSON.stringify(this.content);
}
if (this.content_operations != null) {
operations = {};
ref1 = this.content_operations;
for (n in ref1) {
o = ref1[n];
operations[n] = o.getUid();
}
json.content_operations = operations;
}
return Insert.__super__._encode.call(this, json);
};
@ -485,12 +557,12 @@ module.exports = function() {
})(ops.Operation);
ops.Insert.parse = function(json) {
var content, next, origin, parent, prev, uid;
content = json['content'], uid = json['uid'], prev = json['prev'], next = json['next'], origin = json['origin'], parent = json['parent'];
var content, content_operations, next, origin, parent, prev, uid;
content = json['content'], content_operations = json['content_operations'], uid = json['uid'], prev = json['prev'], next = json['next'], origin = json['origin'], parent = json['parent'];
if (typeof content === "string") {
content = JSON.parse(content);
}
return new this(null, content, parent, uid, prev, next, origin);
return new this(null, content, content_operations, parent, uid, prev, next, origin);
};
ops.Delimiter = (function(superClass) {
extend(Delimiter, superClass);

View File

@ -281,14 +281,14 @@ module.exports = function() {
}
left = right.prev_cl;
if (contents instanceof ops.Operation) {
(new ops.Insert(null, content, void 0, void 0, left, right)).execute();
(new ops.Insert(null, content, null, void 0, void 0, left, right)).execute();
} else {
for (j = 0, len = contents.length; j < len; j++) {
c = contents[j];
if ((c != null) && (c._name != null) && (c._getModel != null)) {
c = c._getModel(this.custom_types, this.operations);
}
tmp = (new ops.Insert(null, c, void 0, void 0, left, right)).execute();
tmp = (new ops.Insert(null, c, null, void 0, void 0, left, right)).execute();
left = tmp;
}
}
@ -337,7 +337,7 @@ module.exports = function() {
position: op.getPosition(),
object: this.getCustomType(),
changedBy: op.uid.creator,
value: getContentType(op.content)
value: getContentType(op.val())
}
]);
};
@ -366,17 +366,38 @@ module.exports = function() {
ops.Composition = (function(superClass) {
extend(Composition, superClass);
function Composition(custom_type, composition_value, uid, tmp_composition_ref) {
function Composition(custom_type, _composition_value, composition_value_operations, uid, tmp_composition_ref) {
var n, o;
this._composition_value = _composition_value;
console.log("delete this ...");
this.constructed_with = [custom_type, this._composition_value, composition_value_operations, uid, tmp_composition_ref];
Composition.__super__.constructor.call(this, custom_type, uid);
if (tmp_composition_ref != null) {
this.tmp_composition_ref = tmp_composition_ref;
} else {
this.composition_ref = this.end.prev_cl;
}
if (composition_value_operations != null) {
this.composition_value_operations = {};
for (n in composition_value_operations) {
o = composition_value_operations[n];
this.saveOperation(n, o, '_composition_value');
}
}
}
Composition.prototype.type = "Composition";
Composition.prototype.execute = function() {
if (this.validateSavedOperations()) {
this.getCustomType()._setCompositionValue(this._composition_value);
delete this._composition_value;
return Composition.__super__.execute.apply(this, arguments);
} else {
return false;
}
};
Composition.prototype.callOperationSpecificInsertEvents = function(op) {
var o;
if (this.tmp_composition_ref != null) {
@ -394,7 +415,7 @@ module.exports = function() {
return;
}
if (this.composition_ref.next_cl === op) {
op.undo_delta = this.getCustomType()._apply(op.content);
op.undo_delta = this.getCustomType()._apply(op.val());
} else {
o = this.end.prev_cl;
while (o !== op) {
@ -402,7 +423,7 @@ module.exports = function() {
o = o.prev_cl;
}
while (o !== this.end) {
o.undo_delta = this.getCustomType()._apply(o.content);
o.undo_delta = this.getCustomType()._apply(o.val());
o = o.next_cl;
}
}
@ -418,16 +439,26 @@ module.exports = function() {
Composition.prototype.callOperationSpecificDeleteEvents = function(op, del_op) {};
Composition.prototype.applyDelta = function(delta) {
(new ops.Insert(null, delta, this, null, this.end.prev_cl, this.end)).execute();
Composition.prototype.applyDelta = function(delta, operations) {
(new ops.Insert(null, delta, operations, this, null, this.end.prev_cl, this.end)).execute();
return void 0;
};
Composition.prototype._encode = function(json) {
var custom, n, o, ref;
if (json == null) {
json = {};
}
json.composition_value = JSON.stringify(this.getCustomType()._getCompositionValue());
custom = this.getCustomType()._getCompositionValue();
json.composition_value = custom.composition_value;
if (custom.composition_value_operations != null) {
json.composition_value_operations = {};
ref = custom.composition_value_operations;
for (n in ref) {
o = ref[n];
json.composition_value_operations[n] = o.getUid();
}
}
if (this.composition_ref != null) {
json.composition_ref = this.composition_ref.getUid();
} else {
@ -440,9 +471,9 @@ module.exports = function() {
})(ops.ListManager);
ops.Composition.parse = function(json) {
var composition_ref, composition_value, custom_type, uid;
uid = json['uid'], custom_type = json['custom_type'], composition_value = json['composition_value'], composition_ref = json['composition_ref'];
return new this(custom_type, JSON.parse(composition_value), uid, composition_ref);
var composition_ref, composition_value, composition_value_operations, custom_type, uid;
uid = json['uid'], custom_type = json['custom_type'], composition_value = json['composition_value'], composition_value_operations = json['composition_value_operations'], composition_ref = json['composition_ref'];
return new this(custom_type, composition_value, composition_value_operations, uid, composition_ref);
};
ops.ReplaceManager = (function(superClass) {
extend(ReplaceManager, superClass);
@ -516,7 +547,7 @@ module.exports = function() {
ReplaceManager.prototype.replace = function(content, replaceable_uid) {
var o, relp;
o = this.getLastOperation();
relp = (new ops.Insert(null, content, this, replaceable_uid, o, o.next_cl)).execute();
relp = (new ops.Insert(null, content, null, this, replaceable_uid, o, o.next_cl)).execute();
return void 0;
};

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -182,7 +182,7 @@ module.exports = ()->
# @param {String} name The name of the operation. After calling this function op is accessible via this[name].
# @param {Operation} op An Operation object
#
saveOperation: (name, op)->
saveOperation: (name, op, base = "this")->
#
# Every instance of $Operation must have an $execute function.
@ -194,11 +194,20 @@ module.exports = ()->
else if op.execute? or not (op.op_number? and op.creator?)
# is instantiated, or op is string. Currently "Delimiter" is saved as string
# (in combination with @parent you can retrieve the delimiter..)
@[name] = op
if base is "this"
@[name] = op
else
dest = @[base]
paths = name.split("/")
last_path = paths.pop()
for path in paths
dest = dest[path]
dest[last_path] = op
else
# not initialized. Do it when calling $validateSavedOperations()
@unchecked ?= {}
@unchecked[name] = op
@unchecked[base] ?= {}
@unchecked[base][name] = op
#
# @private
@ -210,13 +219,23 @@ module.exports = ()->
validateSavedOperations: ()->
uninstantiated = {}
success = @
for name, op_uid of @unchecked
op = @HB.getOperation op_uid
if op
@[name] = op
else
uninstantiated[name] = op_uid
success = false
for base_name, base of @unchecked
for name, op_uid of base
op = @HB.getOperation op_uid
if op
if base_name is "this"
@[name] = op
else
dest = @[base_name]
paths = name.split("/")
last_path = paths.pop()
for path in paths
dest = dest[path]
dest[last_path] = op
else
uninstantiated[base_name] ?= {}
uninstantiated[base_name][name] = op_uid
success = false
delete @unchecked
if not success
@unchecked = uninstantiated
@ -305,7 +324,7 @@ module.exports = ()->
# @param {Operation} prev_cl The predecessor of this operation in the complete-list (cl)
# @param {Operation} next_cl The successor of this operation in the complete-list (cl)
#
constructor: (custom_type, content, parent, uid, prev_cl, next_cl, origin)->
constructor: (custom_type, content, content_operations, parent, uid, prev_cl, next_cl, origin)->
# see encode to see, why we are doing it this way
if content is undefined
# nop
@ -313,6 +332,10 @@ module.exports = ()->
@saveOperation 'content', content
else
@content = content
if content_operations?
@content_operations = {}
for name, op of content_operations
@saveOperation name, op, 'content_operations'
@saveOperation 'parent', parent
@saveOperation 'prev_cl', prev_cl
@saveOperation 'next_cl', next_cl
@ -325,8 +348,19 @@ module.exports = ()->
type: "Insert"
val: ()->
if @content? and @content.getCustomType?
@content.getCustomType()
if @content?
if @content.getCustomType?
@content.getCustomType()
else if @content.constructor is Object
content = {}
for n,v of @content
content[n] = v
if @content_operations?
for n,v of @content_operations
content[n] = v
content
else
@content
else
@content
@ -518,14 +552,20 @@ module.exports = ()->
json.parent = @parent.getUid()
if @content?.getUid?
json['content'] = @content.getUid()
json.content = @content.getUid()
else
json['content'] = JSON.stringify @content
json.content = JSON.stringify @content
if @content_operations?
operations = {}
for n,o of @content_operations
operations[n] = o.getUid()
json.content_operations = operations
super json
ops.Insert.parse = (json)->
{
'content' : content
'content_operations' : content_operations
'uid' : uid
'prev': prev
'next': next
@ -534,7 +574,7 @@ module.exports = ()->
} = json
if typeof content is "string"
content = JSON.parse(content)
new this null, content, parent, uid, prev, next, origin
new this null, content, content_operations, parent, uid, prev, next, origin
#
# @nodoc

View File

@ -238,12 +238,12 @@ module.exports = ()->
# TODO: always expect an array as content. Then you can combine this with the other option (else)
if contents instanceof ops.Operation
(new ops.Insert null, content, undefined, undefined, left, right).execute()
(new ops.Insert null, content, null, undefined, undefined, left, right).execute()
else
for c in contents
if c? and c._name? and c._getModel?
c = c._getModel(@custom_types, @operations)
tmp = (new ops.Insert null, c, undefined, undefined, left, right).execute()
tmp = (new ops.Insert null, c, null, undefined, undefined, left, right).execute()
left = tmp
@
@ -290,7 +290,7 @@ module.exports = ()->
position: op.getPosition()
object: @getCustomType()
changedBy: op.uid.creator
value: getContentType op.content
value: getContentType op.val()
]
callOperationSpecificDeleteEvents: (op, del_op)->
@ -316,31 +316,60 @@ module.exports = ()->
class ops.Composition extends ops.ListManager
constructor: (custom_type, @composition_value, uid, composition_ref)->
constructor: (custom_type, @_composition_value, composition_value_operations, uid, tmp_composition_ref)->
# we can't use @seveOperation 'composition_ref', tmp_composition_ref here,
# because then there is a "loop" (insertion refers to parant, refers to insertion..)
# This is why we have to check in @callOperationSpecificInsertEvents until we find it
console.log("delete this ...")
this.constructed_with = [custom_type, @_composition_value, composition_value_operations, uid, tmp_composition_ref] # debug!
super custom_type, uid
if composition_ref
@saveOperation 'composition_ref', composition_ref
if tmp_composition_ref?
@tmp_composition_ref = tmp_composition_ref
else
@composition_ref = @beginning
@composition_ref = @end.prev_cl
if composition_value_operations?
@composition_value_operations = {}
for n,o of composition_value_operations
@saveOperation n, o, '_composition_value'
type: "Composition"
val: ()->
@composition_value
#
# @private
# @see Operation.execute
#
execute: ()->
if @validateSavedOperations()
@getCustomType()._setCompositionValue @_composition_value
delete @_composition_value
super
else
false
#
# This is called, when the Insert-operation was successfully executed.
#
callOperationSpecificInsertEvents: (op)->
if @tmp_composition_ref?
if op.uid.creator is @tmp_composition_ref.creator and op.uid.op_number is @tmp_composition_ref.op_number
@composition_ref = op
delete @tmp_composition_ref
o = op.next_cl
while o.next_cl?
if not o.isDeleted()
@callOperationSpecificInsertEvents o
o = o.next_cl
return
if @composition_ref.next_cl is op
op.undo_delta = @getCustomType()._apply op.content
op.undo_delta = @getCustomType()._apply op.val()
else
o = @end.prev_cl
while o isnt op
@getCustomType()._unapply o.undo_delta
o = o.prev_cl
while o isnt @end
o.undo_delta = @getCustomType()._apply o.content
o.undo_delta = @getCustomType()._apply o.val()
o = o.next_cl
@composition_ref = @end.prev_cl
@ -361,16 +390,24 @@ module.exports = ()->
#
# @param delta The delta that is applied to the composition_value
#
applyDelta: (delta)->
(new ops.Insert null, delta, @, null, @end.prev_cl, @end).execute()
applyDelta: (delta, operations)->
(new ops.Insert null, delta, operations, @, null, @end.prev_cl, @end).execute()
undefined
#
# Encode this operation in such a way that it can be parsed by remote peers.
#
_encode: (json = {})->
json.composition_value = JSON.stringify @composition_value
json.composition_ref = @composition_ref.getUid()
custom = @getCustomType()._getCompositionValue()
json.composition_value = custom.composition_value
if custom.composition_value_operations?
json.composition_value_operations = {}
for n,o of custom.composition_value_operations
json.composition_value_operations[n] = o.getUid()
if @composition_ref?
json.composition_ref = @composition_ref.getUid()
else
json.composition_ref = @tmp_composition_ref
super json
ops.Composition.parse = (json)->
@ -378,9 +415,10 @@ module.exports = ()->
'uid' : uid
'custom_type': custom_type
'composition_value' : composition_value
'composition_value_operations' : composition_value_operations
'composition_ref' : composition_ref
} = json
new this(custom_type, JSON.parse(composition_value), uid, composition_ref)
new this(custom_type, composition_value, composition_value_operations, uid, composition_ref)
#
@ -465,7 +503,7 @@ module.exports = ()->
#
replace: (content, replaceable_uid)->
o = @getLastOperation()
relp = (new ops.Insert null, content, @, replaceable_uid, o, o.next_cl).execute()
relp = (new ops.Insert null, content, null, @, replaceable_uid, o, o.next_cl).execute()
# TODO: delete repl (for debugging)
undefined

File diff suppressed because one or more lines are too long

3
y.js

File diff suppressed because one or more lines are too long