Skip to content

Commit

Permalink
Replaced PathAdapter by streamlined implementation.
Browse files Browse the repository at this point in the history
In DocumentIndexes we use now TreeIndex instances.
The adapter to objects providing a path based API is called DataObject now.
Both are based on lodash get/set/setWith etc.
  • Loading branch information
obuchtala committed Feb 4, 2016
1 parent c0c36a5 commit f787954
Show file tree
Hide file tree
Showing 15 changed files with 414 additions and 356 deletions.
4 changes: 2 additions & 2 deletions doc/model/MemberIndex.js
@@ -1,7 +1,7 @@
'use strict';

var NodeIndex = require('../../model/data/NodeIndex');
var PathAdapter = require('../../util/PathAdapter');
var TreeIndex = require('../../util/TreeIndex');

/**
@class
Expand All @@ -10,7 +10,7 @@ function MemberIndex(doc) {
NodeIndex.apply(this, arguments);

this.doc = doc;
this.index = new PathAdapter();
this.index = new TreeIndex();
}

MemberIndex.Prototype = function() {
Expand Down
19 changes: 6 additions & 13 deletions model/AnchorIndex.js
@@ -1,17 +1,17 @@
'use strict';

var filter = require('lodash/filter');
var PathAdapter = require('../util/PathAdapter');
var TreeIndex = require('../util/TreeIndex');
var ContainerAnnotation = require('./ContainerAnnotation');
var DocumentIndex = require('./DocumentIndex');

var ContainerAnnotationAnchorIndex = function(doc) {
var AnchorIndex = function(doc) {
this.doc = doc;
this.byPath = new PathAdapter.Arrays();
this.byPath = new TreeIndex.Arrays();
this.byId = {};
};

DocumentIndex.extend(ContainerAnnotationAnchorIndex, function() {
DocumentIndex.extend(AnchorIndex, function() {
this.select = function(node) {
return (node instanceof ContainerAnnotation);
};
Expand All @@ -23,14 +23,7 @@ DocumentIndex.extend(ContainerAnnotationAnchorIndex, function() {
};

this.get = function(path, containerName) {
var anchors = this.byPath.get(path) || [];
if (!isArray(anchors)) {
var _anchors = [];
this.byPath._traverse(anchors, [], function(path, anchors) {
_anchors = _anchors.concat(anchors);
});
anchors = _anchors;
}
var anchors = this.byPath.getAll(path);
if (containerName) {
return filter(anchors, function(anchor) {
return (anchor.container === containerName);
Expand Down Expand Up @@ -75,4 +68,4 @@ DocumentIndex.extend(ContainerAnnotationAnchorIndex, function() {

});

module.exports = ContainerAnnotationAnchorIndex;
module.exports = AnchorIndex;
22 changes: 7 additions & 15 deletions model/AnnotationIndex.js
Expand Up @@ -3,7 +3,7 @@
var isString = require('lodash/isString');
var map = require('lodash/map');
var filter = require('lodash/filter');
var PathAdapter = require('../util/PathAdapter');
var TreeIndex = require('../util/TreeIndex');
var PropertyAnnotation = require('./PropertyAnnotation');
var DocumentIndex = require('./DocumentIndex');

Expand All @@ -22,8 +22,8 @@ var DocumentIndex = require('./DocumentIndex');
// aIndex.get(["text_1", "content"], 23, 45);

var AnnotationIndex = function() {
this.byPath = new PathAdapter();
this.byType = new PathAdapter();
this.byPath = new TreeIndex();
this.byType = new TreeIndex();
};

AnnotationIndex.Prototype = function() {
Expand All @@ -42,21 +42,13 @@ AnnotationIndex.Prototype = function() {

// TODO: use object interface? so we can combine filters (path and type)
this.get = function(path, start, end, type) {
var annotations = this.byPath.get(path) || {};
var annotations;
if (isString(path) || path.length === 1) {
// flatten annotations if this is called via node id
var _annos = annotations;
annotations = [];
each(_annos, function(level) {
annotations = annotations.concat(map(level, function(anno) {
return anno;
}));
});
annotations = this.byPath.getAll(path) || {};
} else {
annotations = map(annotations, function(anno) {
return anno;
});
annotations = this.byPath.get(path);
}
annotations = map(annotations);
/* jshint eqnull:true */
// null check for null or undefined
if (start != null) {
Expand Down
6 changes: 3 additions & 3 deletions model/DocumentChange.js
Expand Up @@ -6,7 +6,7 @@ var isArray = require('lodash/isArray');
var map = require('lodash/map');
var oo = require('../util/oo');
var uuid = require('../util/uuid');
var PathAdapter = require('../util/PathAdapter');
var TreeIndex = require('../util/TreeIndex');
var OperationSerializer = require('./data/OperationSerializer');
var ObjectOperation = require('./data/ObjectOperation');

Expand Down Expand Up @@ -92,7 +92,7 @@ DocumentChange.Prototype = function() {
var ops = this.ops;
var created = {};
var deleted = {};
var updated = new PathAdapter.Arrays();
var updated = new TreeIndex();
var i;
for (i = 0; i < ops.length; i++) {
var op = ops[i];
Expand All @@ -107,7 +107,7 @@ DocumentChange.Prototype = function() {
}
if (op.type === "set" || op.type === "update") {
// The old as well the new one is affected
updated.add(op.path, op);
updated.set(op.path, true);
}
}
this.created = created;
Expand Down
31 changes: 10 additions & 21 deletions model/PathEventProxy.js
Expand Up @@ -2,17 +2,15 @@

var oo = require('../util/oo');
var each = require('lodash/each');
var PathAdapter = require('../util/PathAdapter');
var deleteFromArray = require('../util/deleteFromArray');
var TreeIndex = require('../util/TreeIndex');


var NotifyByPathProxy = function(doc) {
this.listeners = new PathAdapter();
var PathEventProxy = function(doc) {
this.listeners = new TreeIndex.Arrays();
this._list = [];
this.doc = doc;
};

NotifyByPathProxy.Prototype = function() {
PathEventProxy.Prototype = function() {

this.onDocumentChanged = function(change, info, doc) {
// stop if no listeners registered
Expand Down Expand Up @@ -61,27 +59,20 @@ NotifyByPathProxy.Prototype = function() {
}
}
}.bind(this));
change.updated.traverse(function(path) {
var key = path.concat(['listeners']);
var scopedListeners = listeners.get(key);
change.updated.forEach(function(_, path) {
var scopedListeners = listeners.get(path);
each(scopedListeners, function(entry) {
entry.method.call(entry.listener, change, info, doc);
});
}.bind(this));
};

this._add = function(path, listener, method) {
var key = path.concat(['listeners']);
var listeners = this.listeners.get(key);
if (!listeners) {
listeners = [];
this.listeners.set(key, listeners);
}
if (!method) {
throw new Error('Invalid argument: expected function but got ' + method);
}
var entry = { path: path, method: method, listener: listener };
listeners.push(entry);
this.listeners.add(path, entry);
this._list.push(entry);
};

Expand All @@ -94,9 +85,7 @@ NotifyByPathProxy.Prototype = function() {
if (this._list[i].listener === listener) {
var entry = this._list[i];
this._list.splice(i, 1);
var key = entry.path.concat(['listeners']);
var listeners = this.listeners.get(key);
deleteFromArray(listeners, entry);
this.listeners.remove(entry.path, entry);
}
}
};
Expand All @@ -113,6 +102,6 @@ NotifyByPathProxy.Prototype = function() {

};

oo.initClass(NotifyByPathProxy);
oo.initClass(PathEventProxy);

module.exports = NotifyByPathProxy;
module.exports = PathEventProxy;
6 changes: 3 additions & 3 deletions model/data/Data.js
Expand Up @@ -4,7 +4,7 @@ var isArray = require('lodash/isArray');
var isString = require('lodash/isString');
var each = require('lodash/each');
var cloneDeep = require('lodash/cloneDeep');
var PathAdapter = require('../../util/PathAdapter');
var DataObject = require('./DataObject');
var EventEmitter = require('../../util/EventEmitter');

/**
Expand All @@ -27,7 +27,7 @@ function Data(schema, options) {
EventEmitter.call(this);

this.schema = schema;
this.nodes = new PathAdapter();
this.nodes = new DataObject();
this.indexes = {};
this.options = options || {};
this.nodeFactory = options.nodeFactory || schema.getNodeFactory();
Expand Down Expand Up @@ -251,7 +251,7 @@ Data.Prototype = function() {
@private
*/
this.reset = function() {
this.nodes = new PathAdapter();
this.nodes.clear();
};

/**
Expand Down
99 changes: 99 additions & 0 deletions model/data/DataObject.js
@@ -0,0 +1,99 @@
'use strict';

var isString = require('lodash/isString');
var isArray = require('lodash/isArray');
var get = require('lodash/get');
var setWith = require('lodash/setWith');
var unset = require('lodash/unset');
var oo = require('../../util/oo');

/*
An object that can be access via path API.
@class
@param {object} [obj] An object to operate on
@example
var obj = new DataObject({a: "aVal", b: {b1: 'b1Val', b2: 'b2Val'}});
*/

function DataObject(root) {
if (root) {
this.__root__ = root;
}
}

DataObject.Prototype = function() {

this.getRoot = function() {
if (this.__root__) {
return this.__root__;
} else {
return this;
}
};

/**
Get value at path
@return {object} The value stored for a given path
@example
obj.get(['b', 'b1']);
=> b1Val
*/
this.get = function(path) {
if (!path) {
return undefined;
}
if (isString(path)) {
return this.getRoot()[path];
}
if (arguments.length > 1) {
path = Array.prototype.slice(arguments, 0);
}
if (!isArray(path)) {
throw new Error('Illegal argument for DataObject.get()');
}
return get(this.getRoot(), path);
};

this.set = function(path, value) {
if (!path) {
throw new Error('Illegal argument: DataObject.set(>path<, value) - path is mandatory.');
}
if (isString(path)) {
this.getRoot()[path] = value;
} else {
setWith(this.getRoot(), path, value);
}
};

this.delete = function(path) {
if (isString(path)) {
delete this.getRoot()[path];
} else if (path.length === 1) {
delete this.getRoot()[path[0]];
} else {
var success = unset(this.getRoot(), path);
if (!success) {
throw new Error('Could not delete property at path' + path);
}
}
};

this.clear = function() {
var root = this.getRoot();
for (var key in root) {
if (root.hasOwnProperty(key)) {
delete root[key];
}
}
};

};

oo.initClass(DataObject);

module.exports = DataObject;

0 comments on commit f787954

Please sign in to comment.