summaryrefslogtreecommitdiffstats
path: root/src/libs/client
diff options
context:
space:
mode:
Diffstat (limited to 'src/libs/client')
-rw-r--r--src/libs/client/ConnectionModel.cpp2
-rw-r--r--src/libs/client/ConnectionModel.h2
-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.am13
-rw-r--r--src/libs/client/ObjectModel.h5
-rw-r--r--src/libs/client/PatchModel.h14
-rw-r--r--src/libs/client/Serializer.cpp525
-rw-r--r--src/libs/client/Serializer.h118
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