/* This file is part of Ingen. Copyright 2007-2012 David Robillard Ingen is free software: you can redistribute it and/or modify it under the terms of the GNU Affero General Public License as published by the Free Software Foundation, either version 3 of the License, or any later version. Ingen is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Affero General Public License for details. You should have received a copy of the GNU Affero General Public License along with Ingen. If not, see . */ #include "raul/log.hpp" #include "raul/PathTable.hpp" #include "ingen/shared/LV2URIMap.hpp" #include "ingen/client/ClientStore.hpp" #include "ingen/client/EdgeModel.hpp" #include "ingen/client/NodeModel.hpp" #include "ingen/client/ObjectModel.hpp" #include "ingen/client/PatchModel.hpp" #include "ingen/client/PluginModel.hpp" #include "ingen/client/PortModel.hpp" #include "ingen/client/SigClientInterface.hpp" #define LOG(s) s << "[ClientStore] " // #define INGEN_CLIENT_STORE_DUMP 1 using namespace std; using namespace Raul; namespace Ingen { using namespace Shared; namespace Client { ClientStore::ClientStore(SharedPtr uris, SharedPtr engine, SharedPtr emitter) : _uris(uris) , _engine(engine) , _emitter(emitter) , _plugins(new Plugins()) { if (!emitter) return; #define CONNECT(signal, method) \ emitter->signal_##signal().connect( \ sigc::mem_fun(this, &ClientStore::method)); CONNECT(object_deleted, del); CONNECT(object_moved, move); CONNECT(put, put); CONNECT(delta, delta); CONNECT(connection, connect); CONNECT(disconnection, disconnect); CONNECT(disconnect_all, disconnect_all); CONNECT(property_change, set_property); } void ClientStore::clear() { Store::clear(); _plugins->clear(); } void ClientStore::add_object(SharedPtr object) { // If we already have "this" object, merge the existing one into the new // one (with precedence to the new values). iterator existing = find(object->path()); if (existing != end()) { PtrCast(existing->second)->set(object); } else { if (!object->path().is_root()) { SharedPtr parent = _object(object->path().parent()); if (parent) { assert(object->path().is_child_of(parent->path())); object->set_parent(parent); parent->add_child(object); assert(parent && (object->parent() == parent)); (*this)[object->path()] = object; _signal_new_object.emit(object); } } else { (*this)[object->path()] = object; _signal_new_object.emit(object); } } typedef Resource::Properties::const_iterator Iterator; for (Iterator i = object->properties().begin(); i != object->properties().end(); ++i) object->signal_property().emit(i->first, i->second); LOG(debug) << "Added " << object->path() << " {" << endl; for (iterator i = begin(); i != end(); ++i) { LOG(debug) << "\t" << i->first << endl; } LOG(debug) << "}" << endl; } SharedPtr ClientStore::remove_object(const Path& path) { iterator i = find(path); if (i != end()) { assert((*i).second->path() == path); SharedPtr result = PtrCast((*i).second); iterator end = find_descendants_end(i); SharedPtr removed = yank(i, end); LOG(debug) << "Removing " << i->first << " {" << endl; for (iterator i = removed->begin(); i != removed->end(); ++i) { LOG(debug) << "\t" << i->first << endl; } LOG(debug) << "}" << endl; if (result) result->signal_destroyed().emit(); if (!result->path().is_root()) { assert(result->parent()); SharedPtr parent = _object(result->path().parent()); if (parent) { parent->remove_child(result); } } assert(!object(path)); return result; } else { return SharedPtr(); } } SharedPtr ClientStore::_plugin(const URI& uri) { assert(uri.length() > 0); Plugins::iterator i = _plugins->find(uri); if (i == _plugins->end()) return SharedPtr(); else return (*i).second; } SharedPtr ClientStore::plugin(const Raul::URI& uri) const { return const_cast(this)->_plugin(uri); } SharedPtr ClientStore::_object(const Path& path) { assert(path.length() > 0); iterator i = find(path); if (i == end()) { return SharedPtr(); } else { SharedPtr model = PtrCast(i->second); assert(model); assert(model->path().is_root() || model->parent()); return model; } } SharedPtr ClientStore::object(const Path& path) const { return const_cast(this)->_object(path); } SharedPtr ClientStore::_resource(const URI& uri) { if (Path::is_path(uri)) return _object(uri.str()); else return _plugin(uri); } SharedPtr ClientStore::resource(const Raul::URI& uri) const { return const_cast(this)->_resource(uri); } void ClientStore::add_plugin(SharedPtr pm) { SharedPtr existing = _plugin(pm->uri()); if (existing) { existing->set(pm); } else { _plugins->insert(make_pair(pm->uri(), pm)); _signal_new_plugin.emit(pm); } } /* ****** Signal Handlers ******** */ void ClientStore::del(const URI& uri) { if (!Raul::Path::is_path(uri)) return; const Raul::Path path(uri.str()); SharedPtr removed = remove_object(path); removed.reset(); LOG(debug) << "Removed object " << path << ", count: " << removed.use_count(); } void ClientStore::move(const Path& old_path_str, const Path& new_path_str) { Path old_path(old_path_str); Path new_path(new_path_str); iterator parent = find(old_path); if (parent == end()) { LOG(Raul::error) << "Failed to find object " << old_path << " to move." << endl; return; } typedef Table > Removed; iterator end = find_descendants_end(parent); SharedPtr removed = yank(parent, end); assert(removed->size() > 0); typedef Table > PathTable; for (PathTable::iterator i = removed->begin(); i != removed->end(); ++i) { const Path& child_old_path = i->first; assert(Path::descendant_comparator(old_path, child_old_path)); Path child_new_path; if (child_old_path == old_path) child_new_path = new_path; else child_new_path = new_path.base() + child_old_path.substr(old_path.length() + 1); LOG(info) << "Renamed " << child_old_path << " -> " << child_new_path << endl; PtrCast(i->second)->set_path(child_new_path); i->first = child_new_path; } cram(*removed.get()); } void ClientStore::put(const URI& uri, const Resource::Properties& properties, Resource::Graph ctx) { typedef Resource::Properties::const_iterator Iterator; #ifdef INGEN_CLIENT_STORE_DUMP LOG(info) << "PUT " << uri << " {" << endl; for (Iterator i = properties.begin(); i != properties.end(); ++i) LOG(info) << '\t' << i->first << " = " << _uris->forge.str(i->second) << " :: " << i->second.type() << endl; LOG(info) << "}" << endl; #endif bool is_patch, is_node, is_port, is_output; ResourceImpl::type(uris(), properties, is_patch, is_node, is_port, is_output); // Check if uri is a plugin Iterator t = properties.find(_uris->rdf_type); if (t != properties.end() && t->second.type() == _uris->forge.URI) { const Atom& type = t->second; const URI& type_uri = type.get_uri(); const Plugin::Type plugin_type = Plugin::type_from_uri(type_uri); if (plugin_type == Plugin::Patch) { is_patch = true; } else if (plugin_type != Plugin::NIL) { SharedPtr p( new PluginModel(uris(), uri, type_uri, properties)); add_plugin(p); return; } } if (!Path::is_valid(uri.str())) { LOG(Raul::error) << "Bad path `" << uri.str() << "'" << endl; return; } const Path path(uri.str()); SharedPtr obj = PtrCast(_object(path)); if (obj) { obj->set_properties(properties); return; } if (path.is_root()) { is_patch = true; } if (is_patch) { SharedPtr model(new PatchModel(uris(), path)); model->set_properties(properties); add_object(model); } else if (is_node) { const Iterator p = properties.find(_uris->ingen_prototype); SharedPtr plug; if (p->second.is_valid() && p->second.type() == _uris->forge.URI) { if (!(plug = _plugin(p->second.get_uri()))) { LOG(warn) << "Unable to find plugin " << p->second.get_uri() << endl; plug = SharedPtr( new PluginModel(uris(), p->second.get_uri(), _uris->ingen_nil, Resource::Properties())); add_plugin(plug); } SharedPtr n(new NodeModel(uris(), plug, path)); n->set_properties(properties); add_object(n); } else { LOG(warn) << "Node " << path << " has no plugin" << endl; } } else if (is_port) { PortModel::Direction pdir = (is_output) ? PortModel::OUTPUT : PortModel::INPUT; const Iterator i = properties.find(_uris->lv2_index); if (i != properties.end() && i->second.type() == _uris->forge.Int) { const uint32_t index = i->second.get_int32(); SharedPtr p( new PortModel(uris(), path, index, pdir)); p->set_properties(properties); add_object(p); } else { LOG(Raul::error) << "Port " << path << " has no index" << endl; } } else { LOG(warn) << "Ignoring object " << path << " with unknown type " << is_patch << " " << is_node << " " << is_port << endl; } } void ClientStore::delta(const URI& uri, const Resource::Properties& remove, const Resource::Properties& add) { typedef Resource::Properties::const_iterator iterator; #ifdef INGEN_CLIENT_STORE_DUMP LOG(info) << "DELTA " << uri << " {" << endl; for (iterator i = remove.begin(); i != remove.end(); ++i) LOG(info) << " - " << i->first << " = " << _uris->forge.str(i->second) << " :: " << i->second.type() << endl; for (iterator i = add.begin(); i != add.end(); ++i) LOG(info) << " + " << i->first << " = " << _uris->forge.str(i->second) << " :: " << i->second.type() << endl; LOG(info) << "}" << endl; #endif if (!Path::is_valid(uri.str())) { LOG(Raul::error) << "Bad path `" << uri.str() << "'" << endl; return; } const Path path(uri.str()); SharedPtr obj = _object(path); if (obj) { obj->remove_properties(remove); obj->add_properties(add); } else { LOG(warn) << "Failed to find object `" << path << "'" << endl; } } void ClientStore::set_property(const URI& subject_uri, const URI& predicate, const Atom& value) { if (subject_uri == _uris->ingen_engine) { LOG(info) << "Engine property " << predicate << " = " << _uris->forge.str(value) << endl; return; } SharedPtr subject = _resource(subject_uri); if (subject) { subject->set_property(predicate, value); } else { SharedPtr plugin = _plugin(subject_uri); if (plugin) plugin->set_property(predicate, value); else LOG(warn) << "Property '" << predicate << "' for unknown object " << subject_uri << endl; } } SharedPtr ClientStore::connection_patch(const Path& tail_path, const Path& head_path) { SharedPtr patch; if (tail_path.parent() == head_path.parent()) patch = PtrCast(_object(tail_path.parent())); if (!patch && tail_path.parent() == head_path.parent().parent()) patch = PtrCast(_object(tail_path.parent())); if (!patch && tail_path.parent().parent() == head_path.parent()) patch = PtrCast(_object(head_path.parent())); if (!patch) patch = PtrCast(_object(tail_path.parent().parent())); if (!patch) LOG(Raul::error) << "Unable to find connection patch " << tail_path << " -> " << head_path << endl; return patch; } bool ClientStore::attempt_connection(const Path& tail_path, const Path& head_path) { SharedPtr tail = PtrCast(_object(tail_path)); SharedPtr head = PtrCast(_object(head_path)); if (tail && head) { SharedPtr patch = connection_patch(tail_path, head_path); SharedPtr cm(new EdgeModel(tail, head)); tail->connected_to(head); head->connected_to(tail); patch->add_connection(cm); return true; } return false; } void ClientStore::connect(const Path& src_path, const Path& dst_path) { attempt_connection(src_path, dst_path); } void ClientStore::disconnect(const Path& src, const Path& dst) { if (!Path::is_path(src) && !Path::is_path(dst)) { std::cerr << "Bad disconnect notification " << src << " => " << dst << std::endl; return; } const Path src_path(src.str()); const Path dst_path(dst.str()); SharedPtr tail = PtrCast(_object(src_path)); SharedPtr head = PtrCast(_object(dst_path)); if (tail) tail->disconnected_from(head); if (head) head->disconnected_from(tail); SharedPtr patch = connection_patch(src_path, dst_path); if (patch) patch->remove_connection(tail.get(), head.get()); } void ClientStore::disconnect_all(const Raul::Path& parent_patch, const Raul::Path& path) { SharedPtr patch = PtrCast(_object(parent_patch)); SharedPtr object = _object(path); if (!patch || !object) { LOG(Raul::error) << "Bad disconnect all notification " << path << " in " << parent_patch << std::endl; return; } const PatchModel::Connections connections = patch->connections(); for (PatchModel::Connections::const_iterator i = connections.begin(); i != connections.end(); ++i) { SharedPtr c = PtrCast(i->second); if (c->tail()->parent() == object || c->head()->parent() == object || c->tail()->path() == path || c->head()->path() == path) { c->tail()->disconnected_from(c->head()); c->head()->disconnected_from(c->tail()); patch->remove_connection(c->tail().get(), c->head().get()); } } } } // namespace Client } // namespace Ingen