diff options
author | David Robillard <d@drobilla.net> | 2006-09-30 06:47:00 +0000 |
---|---|---|
committer | David Robillard <d@drobilla.net> | 2006-09-30 06:47:00 +0000 |
commit | 35a5d92cfcf6815553a0939c3e2bf77c1108fd31 (patch) | |
tree | 456351b4b18d48aba25a2db7218df9be09d4047e /src/libs/client | |
parent | d82dcd232f201b531a0be165ee44aede1bc8a1df (diff) | |
download | ingen-35a5d92cfcf6815553a0939c3e2bf77c1108fd31.tar.gz ingen-35a5d92cfcf6815553a0939c3e2bf77c1108fd31.tar.bz2 ingen-35a5d92cfcf6815553a0939c3e2bf77c1108fd31.zip |
Work on RDF serialization (only (partial) saving so far).
git-svn-id: http://svn.drobilla.net/lad/ingen@146 a436a847-0d15-0410-975c-d299462d15a1
Diffstat (limited to 'src/libs/client')
-rw-r--r-- | src/libs/client/ConnectionModel.cpp | 2 | ||||
-rw-r--r-- | src/libs/client/ConnectionModel.h | 2 | ||||
-rw-r--r-- | src/libs/client/DeprecatedSerializer.cpp (renamed from src/libs/client/PatchLibrarian.cpp) | 280 | ||||
-rw-r--r-- | src/libs/client/DeprecatedSerializer.h (renamed from src/libs/client/PatchLibrarian.h) | 8 | ||||
-rw-r--r-- | src/libs/client/Makefile.am | 13 | ||||
-rw-r--r-- | src/libs/client/ObjectModel.h | 5 | ||||
-rw-r--r-- | src/libs/client/PatchModel.h | 14 | ||||
-rw-r--r-- | src/libs/client/Serializer.cpp | 525 | ||||
-rw-r--r-- | src/libs/client/Serializer.h | 118 |
9 files changed, 679 insertions, 288 deletions
diff --git a/src/libs/client/ConnectionModel.cpp b/src/libs/client/ConnectionModel.cpp index e120207d..e7f66077 100644 --- a/src/libs/client/ConnectionModel.cpp +++ b/src/libs/client/ConnectionModel.cpp @@ -101,6 +101,8 @@ ConnectionModel::patch_path() const return patch_path; } +typedef list<CountedPtr<ConnectionModel> > ConnectionList; + } // namespace Client } // namespace Ingen diff --git a/src/libs/client/ConnectionModel.h b/src/libs/client/ConnectionModel.h index 4c0d18f8..e1530f88 100644 --- a/src/libs/client/ConnectionModel.h +++ b/src/libs/client/ConnectionModel.h @@ -68,6 +68,8 @@ private: CountedPtr<PortModel> _dst_port; }; +typedef list<CountedPtr<ConnectionModel> > ConnectionList; + } // namespace Client } // namespace Ingen diff --git a/src/libs/client/PatchLibrarian.cpp b/src/libs/client/DeprecatedSerializer.cpp index 00687993..1526c527 100644 --- a/src/libs/client/PatchLibrarian.cpp +++ b/src/libs/client/DeprecatedSerializer.cpp @@ -14,7 +14,7 @@ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA */ -#include "PatchLibrarian.h" +#include "DeprecatedSerializer.h" #include <libxml/parser.h> #include <libxml/tree.h> #include <libxml/xpath.h> @@ -56,7 +56,7 @@ namespace Client { * be a good idea to pass as additional_path, in the case of a subpatch. */ string -PatchLibrarian::find_file(const string& filename, const string& additional_path) +DeprecatedSerializer::find_file(const string& filename, const string& additional_path) { string search_path = additional_path + ":" + _patch_search_path; @@ -86,7 +86,7 @@ PatchLibrarian::find_file(const string& filename, const string& additional_path) is.close(); return full_patch_path; } else { - cerr << "[PatchLibrarian] Could not find patch file " << full_patch_path << endl; + cerr << "[DeprecatedSerializer] Could not find patch file " << full_patch_path << endl; } } @@ -95,7 +95,7 @@ PatchLibrarian::find_file(const string& filename, const string& additional_path) string -PatchLibrarian::translate_load_path(const string& path) +DeprecatedSerializer::translate_load_path(const string& path) { std::map<string,string>::iterator t = _load_path_translations.find(path); @@ -108,262 +108,6 @@ PatchLibrarian::translate_load_path(const string& path) } -/** Save a patch from a PatchModel to a filename. - * - * The filename passed is the true filename the patch will be saved to (with no prefixing or anything - * like that), and the patch_model's filename member will be set accordingly. - * - * This will break if: - * - The filename does not have an extension (ie contain a ".") - * - The patch_model has no (Ingen) path - */ -#if 0 -void -PatchLibrarian::save_patch(CountedPtr<PatchModel> patch_model, const string& filename, bool recursive) -{ - assert(filename != ""); - assert(patch_model->path() != ""); - - cout << "Saving patch " << patch_model->path() << " to " << filename << endl; - - if (patch_model->filename() != filename) - cerr << "Warning: Saving patch to file other than filename stored in model." << endl; - - string dir = filename.substr(0, filename.find_last_of("/")); - - NodeModel* nm = NULL; - - xmlDocPtr xml_doc = NULL; - xmlNodePtr xml_root_node = NULL; - xmlNodePtr xml_node = NULL; - xmlNodePtr xml_child_node = NULL; - //xmlNodePtr xml_grandchild_node = NULL; - - xml_doc = xmlNewDoc((xmlChar*)"1.0"); - xml_root_node = xmlNewNode(NULL, (xmlChar*)"patch"); - xmlDocSetRootElement(xml_doc, xml_root_node); - - const size_t temp_buf_length = 255; - char temp_buf[temp_buf_length]; - - string patch_name; - if (patch_model->path() != "/") { - patch_name = patch_model->path().name(); - } else { - patch_name = filename; - if (patch_name.find("/") != string::npos) - patch_name = patch_name.substr(patch_name.find_last_of("/") + 1); - if (patch_name.find(".") != string::npos) - patch_name = patch_name.substr(0, patch_name.find_last_of(".")); - } - - assert(patch_name.length() > 0); - xml_node = xmlNewChild(xml_root_node, NULL, (xmlChar*)"name", - (xmlChar*)patch_name.c_str()); - - snprintf(temp_buf, temp_buf_length, "%zd", patch_model->poly()); - xml_node = xmlNewChild(xml_root_node, NULL, (xmlChar*)"polyphony", (xmlChar*)temp_buf); - - // Write metadata - for (MetadataMap::const_iterator i = patch_model->metadata().begin(); - i != patch_model->metadata().end(); ++i) { - cerr << "FIXME: metadata save" << endl; - // Dirty hack, don't save coordinates in patch file - //if (i->first != "module-x" && i->first != "module-y" - // && i->first != "filename") - // xml_node = xmlNewChild(xml_root_node, NULL, - // (xmlChar*)(*i).first.c_str(), (xmlChar*)(*i).second.c_str()); - - assert((*i).first != "node"); - assert((*i).first != "subpatch"); - assert((*i).first != "name"); - assert((*i).first != "polyphony"); - assert((*i).first != "preset"); - } - - // Save nodes and subpatches - for (NodeModelMap::const_iterator i = patch_model->nodes().begin(); i != patch_model->nodes().end(); ++i) { - nm = i->second.get(); - - if (nm->plugin()->type() == PluginModel::Patch) { // Subpatch - CountedPtr<PatchModel> spm = PtrCast<PatchModel>(i->second); - assert(spm); - - xml_node = xmlNewChild(xml_root_node, NULL, (xmlChar*)"subpatch", NULL); - - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"name", (xmlChar*)spm->path().name().c_str()); - - string ref_filename; - // No path - if (spm->filename() == "") { - ref_filename = spm->path().name() + ".om"; - cerr << "FIXME: subpatch filename" << endl; - //spm->filename(dir +"/"+ ref_filename); - // Absolute path - } else if (spm->filename().substr(0, 1) == "/") { - // Attempt to make it a relative path, if it's undernath this patch's dir - if (dir.substr(0, 1) == "/" && spm->filename().substr(0, dir.length()) == dir) { - ref_filename = spm->filename().substr(dir.length()+1); - } else { // FIXME: not good - ref_filename = spm->filename().substr(spm->filename().find_last_of("/")+1); - cerr << "FIXME: subpatch filename (2)" << endl; - //spm->filename(dir +"/"+ ref_filename); - } - } else { - ref_filename = spm->filename(); - } - - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"filename", (xmlChar*)ref_filename.c_str()); - - snprintf(temp_buf, temp_buf_length, "%zd", spm->poly()); - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"polyphony", (xmlChar*)temp_buf); - - // Write metadata - for (MetadataMap::const_iterator i = nm->metadata().begin(); - i != nm->metadata().end(); ++i) { - cerr << "FIXME: save metadata\n"; - // Dirty hack, don't save metadata that would be in patch file - /*if ((*i).first != "polyphony" && (*i).first != "filename" - && (*i).first != "author" && (*i).first != "description") - xml_child_node = xmlNewChild(xml_node, NULL, - (xmlChar*)(*i).first.c_str(), (xmlChar*)(*i).second.c_str());*/ - } - - if (recursive) - save_patch(spm, spm->filename(), true); - - } else { // Normal node - xml_node = xmlNewChild(xml_root_node, NULL, (xmlChar*)"node", NULL); - - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"name", (xmlChar*)nm->path().name().c_str()); - - if (!nm->plugin()) break; - - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"polyphonic", - (xmlChar*)((nm->polyphonic()) ? "true" : "false")); - - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"type", - (xmlChar*)nm->plugin()->type_string()); - /* - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"plugin-label", - (xmlChar*)(nm->plugin()->plug_label().c_str())); - - if (nm->plugin()->type() != PluginModel::Internal) { - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"library-name", - (xmlChar*)(nm->plugin()->lib_name().c_str())); - }*/ - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"plugin-uri", - (xmlChar*)(nm->plugin()->uri().c_str())); - - // Write metadata - for (MetadataMap::const_iterator i = nm->metadata().begin(); i != nm->metadata().end(); ++i) { - cerr << "FIXME: Save metadata\n"; - /* - // DSSI _hack_ (FIXME: fix OSC to be more like this and not smash DSSI into metadata?) - if ((*i).first.substr(0, 16) == "dssi-configure--") { - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"dssi-configure", NULL); - xml_grandchild_node = xmlNewChild(xml_child_node, NULL, - (xmlChar*)"key", (xmlChar*)(*i).first.substr(16).c_str()); - xml_grandchild_node = xmlNewChild(xml_child_node, NULL, - (xmlChar*)"value", (xmlChar*)(*i).second.c_str()); - } else if ((*i).first == "dssi-program") { - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"dssi-program", NULL); - xml_grandchild_node = xmlNewChild(xml_child_node, NULL, - (xmlChar*)"bank", (xmlChar*)(*i).second.substr(0, (*i).second.find("/")).c_str()); - xml_grandchild_node = xmlNewChild(xml_child_node, NULL, - (xmlChar*)"program", (xmlChar*)(*i).second.substr((*i).second.find("/")+1).c_str()); - } else { - xml_child_node = xmlNewChild(xml_node, NULL, - (xmlChar*)(*i).first.c_str(), (xmlChar*)(*i).second.c_str()); - } - */ - } - - // Write port metadata, if necessary - for (PortModelList::const_iterator i = nm->ports().begin(); i != nm->ports().end(); ++i) { - cerr << "FIXME: save metadata\n"; - /* - const PortModel* const pm = i->get(); - if (pm->is_input() && pm->user_min() != pm->min_val() || pm->user_max() != pm->max_val()) { - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"port", NULL); - xml_grandchild_node = xmlNewChild(xml_child_node, NULL, (xmlChar*)"name", - (xmlChar*)pm->path().name().c_str()); - snprintf(temp_buf, temp_buf_length, "%f", pm->user_min()); - xml_grandchild_node = xmlNewChild(xml_child_node, NULL, (xmlChar*)"user-min", (xmlChar*)temp_buf); - snprintf(temp_buf, temp_buf_length, "%f", pm->user_max()); - xml_grandchild_node = xmlNewChild(xml_child_node, NULL, (xmlChar*)"user-max", (xmlChar*)temp_buf); - }*/ - } - } - } - - // Save connections - - const list<CountedPtr<ConnectionModel> >& cl = patch_model->connections(); - const ConnectionModel* c = NULL; - - for (list<CountedPtr<ConnectionModel> >::const_iterator i = cl.begin(); i != cl.end(); ++i) { - c = (*i).get(); - xml_node = xmlNewChild(xml_root_node, NULL, (xmlChar*)"connection", NULL); - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"source-node", - (xmlChar*)c->src_port_path().parent().name().c_str()); - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"source-port", - (xmlChar*)c->src_port_path().name().c_str()); - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"destination-node", - (xmlChar*)c->dst_port_path().parent().name().c_str()); - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"destination-port", - (xmlChar*)c->dst_port_path().name().c_str()); - } - - // Save control values (ie presets eventually, right now just current control vals) - - xmlNodePtr xml_preset_node = xmlNewChild(xml_root_node, NULL, (xmlChar*)"preset", NULL); - xml_node = xmlNewChild(xml_preset_node, NULL, (xmlChar*)"name", (xmlChar*)"default"); - - PortModel* pm = NULL; - - // Save node port controls - for (NodeModelMap::const_iterator n = patch_model->nodes().begin(); n != patch_model->nodes().end(); ++n) { - nm = n->second.get(); - for (PortModelList::const_iterator p = nm->ports().begin(); p != nm->ports().end(); ++p) { - pm = (*p).get(); - if (pm->is_input() && pm->is_control()) { - float val = pm->value(); - xml_node = xmlNewChild(xml_preset_node, NULL, (xmlChar*)"control", NULL); - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"node-name", - (xmlChar*)nm->path().name().c_str()); - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"port-name", - (xmlChar*)pm->path().name().c_str()); - snprintf(temp_buf, temp_buf_length, "%f", val); - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"value", - (xmlChar*)temp_buf); - } - } - } - - // Save patch port controls - for (PortModelList::const_iterator p = patch_model->ports().begin(); - p != patch_model->ports().end(); ++p) { - pm = (*p).get(); - if (pm->is_input() && pm->is_control()) { - float val = pm->value(); - xml_node = xmlNewChild(xml_preset_node, NULL, (xmlChar*)"control", NULL); - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"port-name", - (xmlChar*)pm->path().name().c_str()); - snprintf(temp_buf, temp_buf_length, "%f", val); - xml_child_node = xmlNewChild(xml_node, NULL, (xmlChar*)"value", - (xmlChar*)temp_buf); - } - } - - xmlSaveFormatFile(filename.c_str(), xml_doc, 1); // 1 == pretty print - - xmlFreeDoc(xml_doc); - xmlCleanupParser(); -} -#endif - - /** Load a patch in to the engine (and client) from a patch file. * * The name and poly from the passed PatchModel are used. If the name is @@ -390,14 +134,14 @@ PatchLibrarian::save_patch(CountedPtr<PatchModel> patch_model, const string& fil * Returns the path of the newly created patch. */ string -PatchLibrarian::load_patch(const string& filename, +DeprecatedSerializer::load_patch(const string& filename, const string& parent_path, const string& name, size_t poly, MetadataMap initial_data, bool existing) { - cerr << "[PatchLibrarian] Loading patch " << filename << "" << endl; + cerr << "[DeprecatedSerializer] Loading patch " << filename << "" << endl; Path path = "/"; // path of the new patch @@ -530,7 +274,7 @@ PatchLibrarian::load_patch(const string& filename, /** Build a NodeModel given a pointer to a Node in a patch file. */ bool -PatchLibrarian::load_node(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) +DeprecatedSerializer::load_node(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) { xmlChar* key; xmlNodePtr cur = node->xmlChildrenNode; @@ -669,8 +413,8 @@ PatchLibrarian::load_node(const Path& parent, xmlDocPtr doc, const xmlNodePtr no } if (path == "") { - cerr << "[PatchLibrarian] Malformed patch file (node tag has empty children)" << endl; - cerr << "[PatchLibrarian] Node ignored." << endl; + cerr << "[DeprecatedSerializer] Malformed patch file (node tag has empty children)" << endl; + cerr << "[DeprecatedSerializer] Node ignored." << endl; return false; } @@ -753,7 +497,7 @@ PatchLibrarian::load_node(const Path& parent, xmlDocPtr doc, const xmlNodePtr no bool -PatchLibrarian::load_subpatch(const Path& parent, xmlDocPtr doc, const xmlNodePtr subpatch) +DeprecatedSerializer::load_subpatch(const Path& parent, xmlDocPtr doc, const xmlNodePtr subpatch) { xmlChar *key; xmlNodePtr cur = subpatch->xmlChildrenNode; @@ -794,7 +538,7 @@ PatchLibrarian::load_subpatch(const Path& parent, xmlDocPtr doc, const xmlNodePt /** Build a ConnectionModel given a pointer to a connection in a patch file. */ bool -PatchLibrarian::load_connection(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) +DeprecatedSerializer::load_connection(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) { xmlChar *key; xmlNodePtr cur = node->xmlChildrenNode; @@ -843,7 +587,7 @@ PatchLibrarian::load_connection(const Path& parent, xmlDocPtr doc, const xmlNode /** Build a PresetModel given a pointer to a preset in a patch file. */ bool -PatchLibrarian::load_preset(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) +DeprecatedSerializer::load_preset(const Path& parent, xmlDocPtr doc, const xmlNodePtr node) { cerr << "FIXME: load preset\n"; #if 0 diff --git a/src/libs/client/PatchLibrarian.h b/src/libs/client/DeprecatedSerializer.h index 893d5cdc..645c03f7 100644 --- a/src/libs/client/PatchLibrarian.h +++ b/src/libs/client/DeprecatedSerializer.h @@ -38,14 +38,14 @@ class PresetModel; class ModelEngineInterface; -/** Handles all patch saving and loading. +/** Loads deprecated (XML) patch files (from the Om days). * * \ingroup IngenClient */ -class PatchLibrarian +class DeprecatedSerializer { public: - PatchLibrarian(CountedPtr<ModelEngineInterface> engine) + DeprecatedSerializer(CountedPtr<ModelEngineInterface> engine) : _patch_search_path("."), _engine(engine) { assert(_engine); @@ -56,8 +56,6 @@ public: string find_file(const string& filename, const string& additional_path = ""); - //void save_patch(CountedPtr<PatchModel> patch_model, const string& filename, bool recursive); - string load_patch(const string& filename, const string& parent_path, const string& name, diff --git a/src/libs/client/Makefile.am b/src/libs/client/Makefile.am index 42f1fddc..47e675c2 100644 --- a/src/libs/client/Makefile.am +++ b/src/libs/client/Makefile.am @@ -1,11 +1,12 @@ AM_CXXFLAGS = -I$(top_srcdir)/src/common if BUILD_CLIENT_LIB -noinst_LTLIBRARIES = libomclient.la +noinst_LTLIBRARIES = libingenclient.la -libomclient_la_CXXFLAGS = -I$(top_srcdir)/src/common -DPKGDATADIR=\"$(pkgdatadir)\" $(LXML2_CFLAGS) $(LSIGCPP_CFLAGS) +libingenclient_la_CXXFLAGS = -I$(top_srcdir)/src/common -DPKGDATADIR=\"$(pkgdatadir)\" @LXML2_CFLAGS@ @REDLAND_CFLAGS@ @LSIGCPP_CFLAGS@ +libingenclient_la_LIBADD = @LXML2_LIBS@ @LOSC_LIBS@ @REDLAND_LIBS@ @LSIGCPP_LIBS@ -libomclient_la_SOURCES = \ +libingenclient_la_SOURCES = \ ClientInterface.h \ OSCEngineSender.h \ OSCEngineSender.cpp \ @@ -29,8 +30,10 @@ libomclient_la_SOURCES = \ PatchModel.h \ PatchModel.cpp \ PluginModel.h \ - PatchLibrarian.h \ - PatchLibrarian.cpp \ + Serializer.h \ + Serializer.cpp \ + DeprecatedSerializer.h \ + DeprecatedSerializer.cpp \ ConnectionModel.h \ ConnectionModel.cpp \ Store.h \ diff --git a/src/libs/client/ObjectModel.h b/src/libs/client/ObjectModel.h index 5ef03b85..bc780cba 100644 --- a/src/libs/client/ObjectModel.h +++ b/src/libs/client/ObjectModel.h @@ -64,7 +64,6 @@ public: protected: friend class Store; - friend class PatchLibrarian; // FIXME: remove ObjectModel(const Path& path); @@ -81,8 +80,8 @@ protected: { _metadata[key] = value; metadata_update_sig.emit(key, value); } - Path _path; - CountedPtr<ObjectModel> _parent; + Path _path; + CountedPtr<ObjectModel> _parent; MetadataMap _metadata; diff --git a/src/libs/client/PatchModel.h b/src/libs/client/PatchModel.h index b5f9d428..a7679b15 100644 --- a/src/libs/client/PatchModel.h +++ b/src/libs/client/PatchModel.h @@ -41,8 +41,8 @@ class Store; class PatchModel : public NodeModel { public: - const NodeModelMap& nodes() const { return m_nodes; } - const list<CountedPtr<ConnectionModel> >& connections() const { return m_connections; } + const NodeModelMap& nodes() const { return m_nodes; } + const ConnectionList& connections() const { return m_connections; } CountedPtr<ConnectionModel> get_connection(const string& src_port_path, const string& dst_port_path) const; CountedPtr<NodeModel> get_node(const string& node_name) const; @@ -91,11 +91,11 @@ private: PatchModel(const PatchModel& copy); PatchModel& operator=(const PatchModel& copy); - NodeModelMap m_nodes; - list<CountedPtr<ConnectionModel> > m_connections; - string m_filename; - bool m_enabled; - size_t m_poly; + NodeModelMap m_nodes; + ConnectionList m_connections; + string m_filename; + bool m_enabled; + size_t m_poly; }; typedef map<string, CountedPtr<PatchModel> > PatchModelMap; diff --git a/src/libs/client/Serializer.cpp b/src/libs/client/Serializer.cpp new file mode 100644 index 00000000..e82389d4 --- /dev/null +++ b/src/libs/client/Serializer.cpp @@ -0,0 +1,525 @@ +/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include <algorithm> +#include <iostream> +#include <fstream> +#include <string> +#include <vector> +#include <utility> // pair, make_pair +#include <cassert> +#include <cmath> +#include <cstdlib> // atof +#include <cstring> +#include <redland.h> +#include "Serializer.h" +#include "PatchModel.h" +#include "NodeModel.h" +#include "ConnectionModel.h" +#include "PortModel.h" +#include "PresetModel.h" +#include "ModelEngineInterface.h" +#include "PluginModel.h" +#include "util/Path.h" +#include "util/Atom.h" +#include "util/RedlandAtom.h" + +#define U(x) ((const unsigned char*)(x)) + +using std::string; using std::vector; using std::pair; +using std::cerr; using std::cout; using std::endl; + +namespace Ingen { +namespace Client { + + +Serializer::Serializer(CountedPtr<ModelEngineInterface> engine) + : _world(librdf_new_world()) + , _serializer(0) + , _patch_search_path(".") + , _engine(engine) +{ + librdf_world_open(_world); + + //_prefixes["xsd"] = "http://www.w3.org/2001/XMLSchema#"; + _prefixes["rdf"] = "http://www.w3.org/1999/02/22-rdf-syntax-ns#"; + _prefixes["ingen"] = "http://codeson.net/ns/ingen#"; + _prefixes["ingenuity"] = "http://codeson.net/ns/ingenuity#"; +} + + +Serializer::~Serializer() +{ + + librdf_free_world(_world); +} + + +void +Serializer::create() +{ + _serializer = librdf_new_serializer(_world, "rdfxml-abbrev", NULL, librdf_new_uri(_world, U(NS_INGEN()))); + + setup_prefixes(); +} + +void +Serializer::destroy() +{ + librdf_free_serializer(_serializer); + _serializer = NULL; +} + + +void +Serializer::setup_prefixes() +{ + for (map<string,string>::const_iterator i = _prefixes.begin(); i != _prefixes.end(); ++i) { + librdf_serializer_set_namespace(_serializer, + librdf_new_uri(_world, U(i->second.c_str())), i->first.c_str()); + } +} + + +/** Expands the prefix of URI, if the prefix is registered. + * + * If uri is not a valid URI, the empty string is returned (so invalid URIs won't be serialized). + */ +string +Serializer::expand_uri(const string& uri) +{ + // FIXME: slow, stupid + for (map<string,string>::const_iterator i = _prefixes.begin(); i != _prefixes.end(); ++i) + if (uri.substr(0, i->first.length()+1) == i->first + ":") + return i->second + uri.substr(i->first.length()+1); + + /*librdf_uri* redland_uri = librdf_new_uri(_world, U(uri.c_str())); + string ret = (redland_uri) ? uri : ""; + librdf_free_uri(redland_uri); + return ret;*/ + + // FIXME: find a correct way to validate a URI + if (uri.find(":") == string::npos && uri.find("/") == string::npos) + return ""; + else + return uri; +} + + +/** Searches for the filename passed in the path, returning the full + * path of the file, or the empty string if not found. + * + * This function tries to be as friendly a black box as possible - if the path + * passed is an absolute path and the file is found there, it will return + * that path, etc. + * + * additional_path is a list (colon delimeted as usual) of additional + * directories to look in. ie the directory the parent patch resides in would + * be a good idea to pass as additional_path, in the case of a subpatch. + */ +string +Serializer::find_file(const string& filename, const string& additional_path) +{ + string search_path = additional_path + ":" + _patch_search_path; + + // Try to open the raw filename first + std::ifstream is(filename.c_str(), std::ios::in); + if (is.good()) { + is.close(); + return filename; + } + + string directory; + string full_patch_path = ""; + + while (search_path != "") { + directory = search_path.substr(0, search_path.find(':')); + if (search_path.find(':') != string::npos) + search_path = search_path.substr(search_path.find(':')+1); + else + search_path = ""; + + full_patch_path = directory +"/"+ filename; + + std::ifstream is; + is.open(full_patch_path.c_str(), std::ios::in); + + if (is.good()) { + is.close(); + return full_patch_path; + } else { + cerr << "[Serializer] Could not find patch file " << full_patch_path << endl; + } + } + + return ""; +} + + +/** Save a patch from a PatchModel to a filename. + * + * The filename passed is the true filename the patch will be saved to (with no prefixing or anything + * like that), and the patch_model's filename member will be set accordingly. + * + * FIXME: make this take a URI. network loading for free. + * + * This will break if: + * - The filename does not have an extension (ie contain a ".") + * - The patch_model has no (Ingen) path + */ +void +Serializer::save_patch(CountedPtr<PatchModel> patch_model, const string& filename, bool recursive) +{ + librdf_storage* storage = librdf_new_storage(_world, "hashes", NULL, "hash-type='memory'"); + + librdf_model* model = librdf_new_model(_world, storage, NULL); + assert(model); + + const string uri = string("file://") + filename; + add_patch_to_rdf(model, patch_model, uri); + + create(); + + //librdf_serializer_serialize_model_to_file_handle(serializer, stdout, NULL, model); + librdf_serializer_serialize_model_to_file(_serializer, + filename.c_str(), NULL, model); + + destroy(); + + librdf_storage_close(storage); + librdf_free_storage(storage); + librdf_free_model(model); +} + + +void +Serializer::add_resource_to_rdf(librdf_model* rdf, + const string& subject_uri, const string& predicate_uri, const string& object_uri) +{ + + librdf_node* subject = librdf_new_node_from_uri_string(_world, U(subject_uri.c_str())); + add_resource_to_rdf(rdf, subject, predicate_uri, object_uri); +} + +void +Serializer::add_resource_to_rdf(librdf_model* rdf, + librdf_node* subject, const string& predicate_uri, const string& object_uri) +{ + + librdf_node* predicate = librdf_new_node_from_uri_string(_world, U(predicate_uri.c_str())); + librdf_node* object = librdf_new_node_from_uri_string(_world, U(object_uri.c_str())); + + librdf_model_add(rdf, subject, predicate, object); +} + + +void +Serializer::add_atom_to_rdf(librdf_model* rdf, + const string& subject_uri, const string& predicate_uri, const Atom& atom) +{ + librdf_node* subject = librdf_new_node_from_uri_string(_world, U(subject_uri.c_str())); + librdf_node* predicate = librdf_new_node_from_uri_string(_world, U(predicate_uri.c_str())); + librdf_node* object = RedlandAtom::atom_to_node(_world, atom); + + librdf_model_add(rdf, subject, predicate, object); +} + + +void +Serializer::add_patch_to_rdf(librdf_model* rdf, + CountedPtr<PatchModel> patch, const string& uri_unused) +{ + const string uri = "#"; + + add_resource_to_rdf(rdf, + uri.c_str(), + NS_RDF("type"), + NS_INGEN("Patch")); + + if (patch->path().name().length() > 0) { + add_atom_to_rdf(rdf, + uri.c_str(), + NS_INGEN("name"), + Atom(patch->path().name().c_str())); + } + + add_atom_to_rdf(rdf, + uri.c_str(), + NS_INGEN("polyphony"), + Atom((int)patch->poly())); + + for (NodeModelMap::const_iterator n = patch->nodes().begin(); n != patch->nodes().end(); ++n) { + add_node_to_rdf(rdf, n->second, "#"); + add_resource_to_rdf(rdf, "#", NS_INGEN("node"), uri + n->second->path().name()); + } + + for (PortModelList::const_iterator p = patch->ports().begin(); p != patch->ports().end(); ++p) { + add_port_to_rdf(rdf, *p, uri); + add_resource_to_rdf(rdf, "#", NS_INGEN("port"), uri + (*p)->path().name()); + } + + for (ConnectionList::const_iterator c = patch->connections().begin(); c != patch->connections().end(); ++c) { + add_connection_to_rdf(rdf, *c, uri); + } + + //_engine->set_metadata(patch->path(), "uri", uri); +} + + +void +Serializer::add_node_to_rdf(librdf_model* rdf, + CountedPtr<NodeModel> node, const string ns_prefix) +{ + const string node_uri = ns_prefix + node->path().name(); + + add_resource_to_rdf(rdf, + node_uri.c_str(), + NS_RDF("type"), + NS_INGEN("Node")); + + /*add_atom_to_rdf(rdf, + node_uri_ref.c_str(), + NS_INGEN("name"), + Atom(node->path().name()));*/ + + for (PortModelList::const_iterator p = node->ports().begin(); p != node->ports().end(); ++p) { + add_port_to_rdf(rdf, *p, node_uri + "/"); + add_resource_to_rdf(rdf, node_uri, NS_INGEN("port"), node_uri + "/" + (*p)->path().name()); + } + + for (MetadataMap::const_iterator m = node->metadata().begin(); m != node->metadata().end(); ++m) { + if (expand_uri(m->first) != "") { + add_atom_to_rdf(rdf, + node_uri.c_str(), + expand_uri(m->first.c_str()).c_str(), + m->second); + } + } +} + + +void +Serializer::add_port_to_rdf(librdf_model* rdf, + CountedPtr<PortModel> port, const string ns_prefix) +{ + const string port_uri_ref = ns_prefix + port->path().name(); + + add_resource_to_rdf(rdf, + port_uri_ref.c_str(), + NS_RDF("type"), + NS_INGEN("Port")); + + for (MetadataMap::const_iterator m = port->metadata().begin(); m != port->metadata().end(); ++m) { + if (expand_uri(m->first) != "") { + add_atom_to_rdf(rdf, + port_uri_ref.c_str(), + expand_uri(m->first).c_str(), + m->second); + } + } +} + + +void +Serializer::add_connection_to_rdf(librdf_model* rdf, + CountedPtr<ConnectionModel> connection, const string ns_prefix) +{ + librdf_node* c = librdf_new_node(_world); + + const string src_port_rel_path = connection->src_port_path().substr(connection->patch_path().length()); + const string dst_port_rel_path = connection->dst_port_path().substr(connection->patch_path().length()); + + librdf_statement* s = librdf_new_statement_from_nodes(_world, c, + librdf_new_node_from_uri_string(_world, U(NS_INGEN("source"))), + librdf_new_node_from_uri_string(_world, U((ns_prefix + src_port_rel_path).c_str()))); + librdf_model_add_statement(rdf, s); + + librdf_statement* d = librdf_new_statement_from_nodes(_world, c, + librdf_new_node_from_uri_string(_world, U(NS_INGEN("destination"))), + librdf_new_node_from_uri_string(_world, U((ns_prefix + dst_port_rel_path).c_str()))); + librdf_model_add_statement(rdf, d); + + add_resource_to_rdf(rdf, c, NS_RDF("type"), NS_INGEN("Connection")); +} + + +/** Load a patch in to the engine (and client) from a patch file. + * + * The name and poly from the passed PatchModel are used. If the name is + * the empty string, the name will be loaded from the file. If the poly + * is 0, it will be loaded from file. Otherwise the given values will + * be used. + * + * @param wait If true the patch will be checked for existence before + * loading everything in to it (to prevent messing up existing patches + * that exist at the path this one should load as). + * + * @param existing If true, the patch will be loaded into a currently + * existing patch (ie a merging will take place). Errors will result + * if Nodes of conflicting names exist. + * + * @param parent_path Patch to load this patch as a child of (empty string to load + * to the root patch) + * + * @param name Name of this patch (loaded/generated if the empty string) + * + * @param initial_data will be set last, so values passed there will override + * any values loaded from the patch file. + * + * Returns the path of the newly created patch. + */ +string +Serializer::load_patch(const string& filename, + const string& parent_path, + const string& name, + size_t poly, + MetadataMap initial_data, + bool existing) +{ +#if 0 + cerr << "[Serializer] Loading patch " << filename << "" << endl; + + Path path = "/"; // path of the new patch + + const bool load_name = (name == ""); + const bool load_poly = (poly == 0); + + if (initial_data.find("filename") == initial_data.end()) + initial_data["filename"] = Atom(filename.c_str()); // FIXME: URL? + + xmlDocPtr doc = xmlParseFile(filename.c_str()); + + if (!doc) { + cerr << "Unable to parse patch file." << endl; + return ""; + } + + xmlNodePtr cur = xmlDocGetRootElement(doc); + + if (!cur) { + cerr << "Empty document." << endl; + xmlFreeDoc(doc); + return ""; + } + + if (xmlStrcmp(cur->name, (const xmlChar*) "patch")) { + cerr << "File is not an Ingen patch file (root node != <patch>)" << endl; + xmlFreeDoc(doc); + return ""; + } + + xmlChar* key = NULL; + cur = cur->xmlChildrenNode; + + // Load Patch attributes + while (cur != NULL) { + key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1); + + if ((!xmlStrcmp(cur->name, (const xmlChar*)"name"))) { + if (load_name) { + assert(key != NULL); + if (parent_path != "") + path = Path(parent_path).base() + Path::nameify((char*)key); + } + } else if ((!xmlStrcmp(cur->name, (const xmlChar*)"polyphony"))) { + if (load_poly) { + poly = atoi((char*)key); + } + } else if (xmlStrcmp(cur->name, (const xmlChar*)"connection") + && xmlStrcmp(cur->name, (const xmlChar*)"node") + && xmlStrcmp(cur->name, (const xmlChar*)"subpatch") + && xmlStrcmp(cur->name, (const xmlChar*)"filename") + && xmlStrcmp(cur->name, (const xmlChar*)"preset")) { + // Don't know what this tag is, add it as metadata without overwriting + // (so caller can set arbitrary parameters which will be preserved) + if (key) + if (initial_data.find((const char*)cur->name) == initial_data.end()) + initial_data[(const char*)cur->name] = (const char*)key; + } + + xmlFree(key); + key = NULL; // Avoid a (possible?) double free + + cur = cur->next; + } + + if (poly == 0) + poly = 1; + + // Create it, if we're not merging + if (!existing) + _engine->create_patch_with_data(path, poly, initial_data); + + // Load nodes + cur = xmlDocGetRootElement(doc)->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar*)"node"))) + load_node(path, doc, cur); + + cur = cur->next; + } + + // Load subpatches + cur = xmlDocGetRootElement(doc)->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar*)"subpatch"))) { + load_subpatch(path, doc, cur); + } + cur = cur->next; + } + + // Load connections + cur = xmlDocGetRootElement(doc)->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar*)"connection"))) { + load_connection(path, doc, cur); + } + cur = cur->next; + } + + + // Load presets (control values) + cerr << "FIXME: load preset\n"; + /*cur = xmlDocGetRootElement(doc)->xmlChildrenNode; + while (cur != NULL) { + if ((!xmlStrcmp(cur->name, (const xmlChar*)"preset"))) { + load_preset(pm, doc, cur); + assert(preset_model != NULL); + if (preset_model->name() == "default") + _engine->set_preset(pm->path(), preset_model); + } + cur = cur->next; + } + */ + + xmlFreeDoc(doc); + xmlCleanupParser(); + + // Done above.. late enough? + //_engine->set_metadata_map(path, initial_data); + + if (!existing) + _engine->enable_patch(path); + + _load_path_translations.clear(); + + return path; +#endif + return "/FIXME"; +} + +} // namespace Client +} // namespace Ingen diff --git a/src/libs/client/Serializer.h b/src/libs/client/Serializer.h new file mode 100644 index 00000000..b2dbf000 --- /dev/null +++ b/src/libs/client/Serializer.h @@ -0,0 +1,118 @@ +/* This file is part of Ingen. Copyright (C) 2006 Dave Robillard. + * + * Ingen is free software; you can redistribute it and/or modify it under the + * terms of the GNU General Public License as published by the Free Software + * Foundation; either version 2 of the License, or (at your option) 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 General Public License for details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#ifndef PATCHLIBRARIAN_H +#define PATCHLIBRARIAN_H + +#include <map> +#include <utility> +#include <string> +#include <redland.h> +#include <cassert> +#include "util/CountedPtr.h" +#include "util/Path.h" +#include "util/Atom.h" +#include "ObjectModel.h" + +using std::string; + +namespace Ingen { +namespace Client { + +class PatchModel; +class NodeModel; +class PortModel; +class ConnectionModel; +class PresetModel; +class ModelEngineInterface; + + +/** Namespace prefix macros. */ +#define NS_RDF(x) "http://www.w3.org/1999/02/22-rdf-syntax-ns#" x +#define NS_INGEN(x) "http://codeson.net/ns/ingen#" x + + +/** Handles all patch saving and loading. + * + * \ingroup IngenClient + */ +class Serializer +{ +public: + Serializer(CountedPtr<ModelEngineInterface> engine); + ~Serializer(); + + void path(const string& path) { _patch_search_path = path; } + const string& path() { return _patch_search_path; } + + string find_file(const string& filename, const string& additional_path = ""); + + void save_patch(CountedPtr<PatchModel> patch_model, const string& filename, bool recursive); + + string load_patch(const string& filename, + const string& parent_path, + const string& name, + size_t poly, + MetadataMap initial_data, + bool existing = false); + +private: + + // Model -> RDF + + void add_patch_to_rdf(librdf_model* rdf, + CountedPtr<PatchModel> patch, const string& uri); + + void add_node_to_rdf(librdf_model* rdf, + CountedPtr<NodeModel> node, const string ns_prefix=""); + + void add_port_to_rdf(librdf_model* rdf, + CountedPtr<PortModel> port, const string ns_prefix=""); + + void add_connection_to_rdf(librdf_model* rdf, + CountedPtr<ConnectionModel> connection, const string port_ns_prefix=""); + + + // Triple -> RDF + + void add_resource_to_rdf(librdf_model* model, + const string& subject_uri, const string& predicate_uri, const string& object_uri); + + void add_resource_to_rdf(librdf_model* model, + librdf_node* subject, const string& predicate_uri, const string& object_uri); + + void add_atom_to_rdf(librdf_model* model, + const string& subject_uri, const string& predicate_uri, const Atom& atom); + + + void create(); + void destroy(); + void setup_prefixes(); + string expand_uri(const string& uri); + + + librdf_world* _world; + librdf_serializer* _serializer; + string _patch_search_path; + map<string, string> _prefixes; + CountedPtr<ModelEngineInterface> _engine; +}; + + +} // namespace Client +} // namespace Ingen + +#endif // PATCHLIBRARIAN_H |