2015-08-16 22:29:54 +00:00
|
|
|
#include "DiffManager.h"
|
|
|
|
|
|
|
|
#include "DefinitionDiff.h"
|
2016-07-23 20:58:32 +00:00
|
|
|
#include "DefinitionNode.h"
|
2015-08-17 20:55:30 +00:00
|
|
|
#include "DefinitionWatcher.h"
|
2015-11-20 00:07:31 +00:00
|
|
|
#include "Logs.h"
|
2016-07-23 20:58:32 +00:00
|
|
|
#include <algorithm>
|
|
|
|
#include <map>
|
|
|
|
#include <mutex>
|
|
|
|
#include <vector>
|
2015-08-16 22:29:54 +00:00
|
|
|
|
2016-01-18 22:06:50 +00:00
|
|
|
class DiffManager::pimpl {
|
|
|
|
public:
|
|
|
|
pimpl(DefinitionNode *tree) : tree(tree), undone(0) {
|
|
|
|
}
|
|
|
|
|
2016-01-21 22:02:22 +00:00
|
|
|
recursive_mutex lock;
|
2016-01-18 22:06:50 +00:00
|
|
|
DefinitionNode *tree;
|
|
|
|
unsigned long undone;
|
|
|
|
vector<const DefinitionDiff *> diffs;
|
|
|
|
map<const DefinitionNode *, vector<DefinitionWatcher *>> watchers;
|
|
|
|
};
|
|
|
|
|
|
|
|
DiffManager::DiffManager(DefinitionNode *tree) : impl(new pimpl(tree)) {
|
2015-08-16 22:29:54 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
DiffManager::~DiffManager() {
|
2016-01-18 22:06:50 +00:00
|
|
|
// TODO smart pointers
|
|
|
|
for (auto diff : impl->diffs) {
|
2015-08-17 20:55:30 +00:00
|
|
|
delete diff;
|
|
|
|
}
|
2016-01-18 22:06:50 +00:00
|
|
|
impl->diffs.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
unsigned long DiffManager::getDiffCount(int include_undone) const {
|
|
|
|
return include_undone ? impl->diffs.size() : impl->diffs.size() - impl->undone;
|
2015-08-17 20:55:30 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void DiffManager::addWatcher(const DefinitionNode *node, DefinitionWatcher *watcher) {
|
2016-01-21 22:02:22 +00:00
|
|
|
impl->lock.lock();
|
2016-01-18 22:06:50 +00:00
|
|
|
auto &watchers = impl->watchers[node];
|
|
|
|
if (find(watchers.begin(), watchers.end(), watcher) == watchers.end()) {
|
|
|
|
watchers.push_back(watcher);
|
2015-09-21 21:10:43 +00:00
|
|
|
}
|
2016-01-21 22:02:22 +00:00
|
|
|
impl->lock.unlock();
|
|
|
|
}
|
|
|
|
|
|
|
|
void DiffManager::removeWatcher(DefinitionWatcher *watcher) {
|
|
|
|
impl->lock.lock();
|
|
|
|
for (auto &item : impl->watchers) {
|
|
|
|
auto &node_watchers = item.second;
|
|
|
|
node_watchers.erase(remove(node_watchers.begin(), node_watchers.end(), watcher), node_watchers.end());
|
|
|
|
}
|
|
|
|
impl->lock.unlock();
|
2015-09-21 21:10:43 +00:00
|
|
|
}
|
|
|
|
|
2016-01-18 22:06:50 +00:00
|
|
|
unsigned long DiffManager::getWatcherCount(const DefinitionNode *node) {
|
|
|
|
return impl->watchers[node].size();
|
2015-08-16 22:29:54 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void DiffManager::addDiff(DefinitionNode *node, const DefinitionDiff *diff) {
|
2016-01-18 22:06:50 +00:00
|
|
|
while (impl->undone > 0) {
|
2015-08-17 16:18:31 +00:00
|
|
|
// truncate diffs ahead
|
2016-01-18 22:06:50 +00:00
|
|
|
delete impl->diffs.back();
|
|
|
|
impl->diffs.pop_back();
|
|
|
|
impl->undone--;
|
2015-08-17 16:18:31 +00:00
|
|
|
}
|
|
|
|
|
2016-01-18 22:06:50 +00:00
|
|
|
impl->diffs.push_back(diff);
|
2015-08-16 22:29:54 +00:00
|
|
|
|
|
|
|
// TODO Delayed commit (with merge of consecutive diffs)
|
|
|
|
|
2016-01-16 15:21:02 +00:00
|
|
|
Logs::debug("Definition") << "Node changed : " << node->getPath() << endl;
|
|
|
|
node->applyDiff(diff);
|
|
|
|
publishToWatchers(node, diff);
|
2015-08-16 22:29:54 +00:00
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void DiffManager::undo() {
|
2016-01-18 22:06:50 +00:00
|
|
|
if (impl->undone < impl->diffs.size()) {
|
|
|
|
const DefinitionDiff *diff = impl->diffs[impl->diffs.size() - impl->undone - 1];
|
2015-08-16 22:29:54 +00:00
|
|
|
|
|
|
|
// Obtain the node by path and reverse apply diff on it
|
2016-01-18 22:06:50 +00:00
|
|
|
DefinitionNode *node = impl->tree->findByPath(diff->getPath());
|
2015-11-20 00:07:31 +00:00
|
|
|
if (node) {
|
2016-01-18 22:06:50 +00:00
|
|
|
impl->undone++;
|
2015-11-20 00:07:31 +00:00
|
|
|
|
2016-01-16 15:21:02 +00:00
|
|
|
unique_ptr<DefinitionDiff> reversed(diff->newReversed());
|
2015-08-18 23:17:49 +00:00
|
|
|
|
2016-01-16 15:21:02 +00:00
|
|
|
Logs::debug("Definition") << "Node undo : " << node->getPath() << endl;
|
|
|
|
// TODO use reversed ?
|
|
|
|
node->applyDiff(diff, true);
|
|
|
|
publishToWatchers(node, reversed.get());
|
2015-11-20 00:07:31 +00:00
|
|
|
} else {
|
2015-12-13 19:08:38 +00:00
|
|
|
Logs::error("Definition") << "Can't find node to undo diff : " << diff->getPath() << endl;
|
2015-08-18 23:17:49 +00:00
|
|
|
}
|
2015-08-16 22:29:54 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-11-09 21:30:46 +00:00
|
|
|
void DiffManager::redo() {
|
2016-01-18 22:06:50 +00:00
|
|
|
if (impl->undone > 0) {
|
|
|
|
const DefinitionDiff *diff = impl->diffs[impl->diffs.size() - impl->undone];
|
2015-08-16 22:29:54 +00:00
|
|
|
|
|
|
|
// Obtain the node by path and re-apply diff on it
|
2016-01-18 22:06:50 +00:00
|
|
|
DefinitionNode *node = impl->tree->findByPath(diff->getPath());
|
2015-11-20 00:07:31 +00:00
|
|
|
if (node) {
|
2016-01-18 22:06:50 +00:00
|
|
|
impl->undone--;
|
2015-11-20 00:07:31 +00:00
|
|
|
|
2016-01-16 15:21:02 +00:00
|
|
|
Logs::debug("Definition") << "Node redo : " << node->getPath() << endl;
|
2015-11-20 00:07:31 +00:00
|
|
|
node->applyDiff(diff);
|
2016-01-16 15:21:02 +00:00
|
|
|
publishToWatchers(node, diff);
|
2015-11-20 00:07:31 +00:00
|
|
|
} else {
|
2015-12-13 19:08:38 +00:00
|
|
|
Logs::error("Definition") << "Can't find node to redo diff : " << diff->getPath() << endl;
|
2015-08-18 23:17:49 +00:00
|
|
|
}
|
2015-08-16 22:29:54 +00:00
|
|
|
}
|
|
|
|
}
|
2016-01-16 15:21:02 +00:00
|
|
|
|
|
|
|
void DiffManager::publishToWatchers(const DefinitionNode *node, const DefinitionDiff *diff) {
|
2016-01-21 22:02:22 +00:00
|
|
|
impl->lock.lock();
|
|
|
|
|
2016-01-16 15:21:02 +00:00
|
|
|
const DefinitionNode *cnode = node;
|
|
|
|
do {
|
2016-01-18 22:06:50 +00:00
|
|
|
for (auto watcher : impl->watchers[cnode]) {
|
2016-01-16 15:21:02 +00:00
|
|
|
watcher->nodeChanged(node, diff, cnode);
|
|
|
|
}
|
2016-01-21 22:02:22 +00:00
|
|
|
// TODO Parent node signaling should be aggregated (to not receive many nodeChanged calls)
|
2016-01-16 15:21:02 +00:00
|
|
|
cnode = cnode->getParent();
|
|
|
|
} while (cnode);
|
2016-01-21 22:02:22 +00:00
|
|
|
|
|
|
|
impl->lock.unlock();
|
2016-01-16 15:21:02 +00:00
|
|
|
}
|