sfdb append optimization.
[ardour.git] / libs / ardour / audio_library.cc
1 /*
2     Copyright (C) 2003 Paul Davis 
3     Author: Taybin Rutkin
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this program; if not, write to the Free Software
17     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
18
19     $Id$
20 */
21
22 #include <cstdio> // Needed so that libraptor (included in lrdf) won't complain
23 #include <iostream>
24 #include <sstream>
25 #include <cctype>
26
27 #include <lrdf.h>
28
29 #include <pbd/compose.h>
30
31 #include <ardour/ardour.h>
32 #include <ardour/configuration.h>
33 #include <ardour/audio_library.h>
34 #include <ardour/utils.h>
35
36 #include "i18n.h"
37
38 using namespace std;
39 using namespace ARDOUR;
40
41 namespace std {
42 struct UriSorter {
43         bool operator() (string a, string b) const { 
44                 return cmp_nocase(Library->get_label(a), Library->get_label(b)) == -1; 
45         }
46 }; 
47 };
48
49 static char* GROUP = "http://ardour.org/ontology/Group";
50 static char* SOUNDFILE = "http://ardour.org/ontology/Soundfile";
51 static char* hasFile = "http://ardour.org/ontology/hasFile";
52 static char* memberOf = "http://ardour.org/ontology/memberOf";
53 static char* subGroupOf = "http://ardour.org/ontology/subGroupOf";
54
55 AudioLibrary::AudioLibrary ()
56 {
57         src = "file:" + Config->get_user_ardour_path() + "sfdb";
58
59         // workaround for possible bug in raptor that crashes when saving to a
60         // non-existant file.
61         touch_file(Config->get_user_ardour_path() + "sfdb");
62
63         lrdf_read_file(src.c_str());
64
65         lrdf_statement pattern;
66
67         pattern.subject = GROUP;
68         pattern.predicate = RDF_TYPE;
69         pattern.object = RDFS_CLASS;
70         pattern.object_type = lrdf_uri;
71
72         lrdf_statement* matches = lrdf_matches(&pattern);
73
74         // if empty DB, create basic schema
75         if (matches == 0) {
76                 initialize_db ();
77                 save_changes();
78         } 
79
80         lrdf_free_statements(matches);
81 }
82
83 AudioLibrary::~AudioLibrary ()
84 {
85 }
86
87 void
88 AudioLibrary::initialize_db ()
89 {
90         // define ardour:Group
91         lrdf_add_triple(src.c_str(), GROUP, RDF_TYPE, RDFS_CLASS, lrdf_uri);
92         // define ardour:Soundfile
93         lrdf_add_triple(src.c_str(), SOUNDFILE, RDF_TYPE, RDFS_CLASS, lrdf_uri);
94
95         // add intergral fields
96         add_field("channels");
97         add_field("samplerate");
98         add_field("resolution");
99         add_field("format");
100 }
101
102 void
103 AudioLibrary::save_changes ()
104 {
105         if (lrdf_export_by_source(src.c_str(), src.substr(5).c_str())) {
106                 warning << string_compose(_("Could not open %1.  Audio Library not saved"), src) << endmsg;
107         }
108 }
109
110 string
111 AudioLibrary::add_group (string group, string parent_uri)
112 {
113         string local_group(string_compose("file:sfbd/group/%1", get_uid()));
114
115         lrdf_add_triple(src.c_str(), local_group.c_str(), 
116                                         RDFS_BASE "label", group.c_str(), lrdf_literal);
117
118         if (parent_uri == ""){
119                 lrdf_add_triple(src.c_str(), local_group.c_str(), 
120                                                 subGroupOf, GROUP, lrdf_uri);
121         } else {
122                 lrdf_add_triple(src.c_str(), local_group.c_str(), 
123                                                 subGroupOf, parent_uri.c_str(), lrdf_uri);
124         }
125
126          added_group(local_group, parent_uri); /* EMIT SIGNAL */
127
128         return local_group;
129 }
130
131 void
132 AudioLibrary::remove_group (string uri)
133 {
134         list<string> items;
135         list<string>::iterator i;
136
137         get_members(items, uri);
138         for (i = items.begin(); i != items.end(); ++i) {
139                 remove_member(*i);
140         }
141         
142         items.clear();
143         
144         get_groups(items, uri);
145         for (i = items.begin(); i != items.end(); ++i) {
146                 remove_group(*i);
147         }
148
149         lrdf_remove_uri_matches(uri.c_str());
150
151          removed_group(uri); /* EMIT SIGNAL */
152 }
153
154 void
155 AudioLibrary::get_groups (list<string>& groups, string parent_uri)
156 {
157         lrdf_statement pattern;
158
159         pattern.subject = 0;
160         pattern.predicate = subGroupOf;
161         if (parent_uri == ""){
162                 pattern.object = strdup(GROUP);
163         } else {
164                 pattern.object = strdup(parent_uri.c_str());
165         }
166
167         lrdf_statement* matches = lrdf_matches(&pattern);
168
169         lrdf_statement* current = matches;
170         while (current != 0) {
171                 groups.push_back(current->subject);
172                 current = current->next;
173         }
174
175         lrdf_free_statements(matches);
176         free (pattern.object);
177
178         UriSorter cmp;
179         groups.sort(cmp);
180         groups.unique();
181 }
182
183 string
184 AudioLibrary::add_member (string member, string parent_uri)
185 {
186         string local_member(string_compose("file:sfdb/soundfile/%1", get_uid()));
187         string file_uri(string_compose("file:%1", member));
188
189         lrdf_add_triple(src.c_str(), local_member.c_str(), RDF_TYPE, 
190                         SOUNDFILE, lrdf_uri);
191         lrdf_add_triple(src.c_str(), local_member.c_str(), hasFile,
192                                         file_uri.c_str(), lrdf_uri);
193
194         string::size_type size = member.find_last_of('/');
195         string label = member.substr(++size);
196
197         lrdf_add_triple(src.c_str(), local_member.c_str(), RDFS_BASE "label", 
198                         label.c_str(), lrdf_literal);
199
200         if (parent_uri == ""){
201                 lrdf_add_triple(src.c_str(), local_member.c_str(), memberOf,
202                                 GROUP, lrdf_uri);
203         } else {
204                 lrdf_add_triple(src.c_str(), local_member.c_str(), memberOf, 
205                         parent_uri.c_str(), lrdf_uri);
206         }
207
208         added_member (local_member, parent_uri); /* EMIT SIGNAL */
209
210         return local_member;
211 }
212
213 void
214 AudioLibrary::remove_member (string uri)
215 {
216         lrdf_remove_uri_matches (uri.c_str());
217
218          removed_member(uri); /* EMIT SIGNAL */
219 }
220
221 void
222 AudioLibrary::get_members (list<string>& members, string parent_uri)
223 {
224         lrdf_statement pattern;
225
226         pattern.subject = 0;
227         pattern.predicate = memberOf;
228         if (parent_uri == ""){
229                 pattern.object = strdup(GROUP);
230         } else {
231                 pattern.object = strdup(parent_uri.c_str());
232         }
233
234         lrdf_statement* matches = lrdf_matches(&pattern);
235
236         lrdf_statement* current = matches;
237         while (current != 0) {
238                 members.push_back(current->subject);
239                 current = current->next;
240         }
241
242         lrdf_free_statements(matches);
243         free (pattern.object);
244
245         UriSorter cmp;
246         members.sort(cmp);
247         members.unique();
248 }
249
250 void
251 AudioLibrary::search_members_and (list<string>& members, 
252                                                                   const map<string,string>& fields)
253 {
254         lrdf_statement **head;
255         lrdf_statement* pattern = 0;
256         lrdf_statement* old = 0;
257         head = &pattern;
258
259         map<string,string>::const_iterator i;
260         for (i = fields.begin(); i != fields.end(); ++i){
261                 pattern = new lrdf_statement;
262                 pattern->subject = "?";
263                 pattern->predicate = strdup(field_uri(i->first).c_str());
264                 pattern->object = strdup((i->second).c_str());
265                 pattern->next = old;
266
267                 old = pattern;
268         }
269
270         if (*head != 0) {
271                 lrdf_uris* ulist = lrdf_match_multi(*head);
272                 for (uint32_t j = 0; ulist && j < ulist->count; ++j) {
273 //                      printf("AND: %s\n", ulist->items[j]);
274                         members.push_back(ulist->items[j]);
275                 }
276                 lrdf_free_uris(ulist);
277
278                 UriSorter cmp;
279                 members.sort(cmp);
280                 members.unique();
281         }
282
283         // memory clean up
284         pattern = *head;
285         while(pattern){
286                 free(pattern->predicate);
287                 free(pattern->object);
288                 old = pattern;
289                 pattern = pattern->next;
290                 delete old;
291         }
292 }
293
294 void
295 AudioLibrary::search_members_or (list<string>& members, 
296                                                                  const map<string,string>& fields)
297 {
298         map<string,string>::const_iterator i;
299
300         lrdf_statement pattern;
301         for (i = fields.begin(); i != fields.end(); ++i) {
302                 pattern.subject = 0;
303                 pattern.predicate = strdup(field_uri(i->first).c_str());
304                 pattern.object = strdup((i->second).c_str());
305                 pattern.object_type = lrdf_literal;
306
307                 lrdf_statement* matched = lrdf_matches(&pattern);
308
309                 lrdf_statement* old = matched;
310                 while(matched) {
311 //                      printf ("OR: %s\n", matched->subject);
312                         members.push_back(matched->subject);
313                         matched = matched->next;
314                 }
315
316                 free(pattern.predicate);
317                 free(pattern.object);
318                 lrdf_free_statements (old);
319         }
320
321         UriSorter cmp;
322         members.sort(cmp);
323         members.unique();
324 }
325
326 string
327 AudioLibrary::get_member_filename (string uri)
328 {
329         lrdf_statement pattern;
330         pattern.subject = strdup(uri.c_str());
331         pattern.predicate = hasFile;
332         pattern.object = 0;
333         pattern.object_type = lrdf_uri;
334         
335         lrdf_statement* matches = lrdf_matches(&pattern);
336         if (matches) {
337                 string file = matches->object;
338                 lrdf_free_statements(matches);
339
340                 string::size_type pos = file.find(":");
341                 return file.substr(++pos);
342         } else {
343                 warning << _("Could not find member filename") << endmsg;
344                 return "-Unknown-";
345         }
346 }
347
348 void
349 AudioLibrary::add_field (string name)
350 {
351         string local_field = field_uri(name);
352         lrdf_statement pattern;
353         pattern.subject = strdup(local_field.c_str());
354         pattern.predicate = RDF_TYPE;
355         pattern.object = RDF_BASE "Property";
356         pattern.object_type = lrdf_uri;
357
358         if(lrdf_exists_match(&pattern)) {
359                 return;
360         }
361
362         // of type rdf:Property
363         lrdf_add_triple(src.c_str(), local_field.c_str(), RDF_TYPE, 
364                         RDF_BASE "Property", lrdf_uri);
365         // of range ardour:Soundfile
366         lrdf_add_triple(src.c_str(), local_field.c_str(), RDFS_BASE "range",
367                         SOUNDFILE, lrdf_uri);
368         // of domain rdf:Literal
369         lrdf_add_triple(src.c_str(), local_field.c_str(), RDFS_BASE "domain", 
370                                         RDF_BASE "Literal", lrdf_uri);
371
372         set_label (local_field, name);
373         
374          fields_changed(); /* EMIT SIGNAL */
375 }
376
377 void
378 AudioLibrary::get_fields (list<string>& fields)
379 {
380         lrdf_statement pattern;
381
382         pattern.subject = 0;
383         pattern.predicate = RDFS_BASE "range";
384         pattern.object = SOUNDFILE;
385         pattern.object_type = lrdf_uri;
386
387         lrdf_statement* matches = lrdf_matches(&pattern);
388
389         lrdf_statement* current = matches;
390         while (current != 0) {
391                 fields.push_back(get_label(current->subject));
392
393                 current = current->next;
394         }
395
396         lrdf_free_statements(matches);
397
398         fields.sort();
399         fields.unique();
400 }
401
402 void
403 AudioLibrary::remove_field (string name)
404 {
405         lrdf_remove_uri_matches(field_uri(name).c_str());
406          fields_changed (); /* EMIT SIGNAL */
407 }
408
409 string 
410 AudioLibrary::get_field (string uri, string field)
411 {
412         lrdf_statement pattern;
413
414         pattern.subject = strdup(uri.c_str());
415
416         pattern.predicate = strdup(field_uri(field).c_str());
417
418         pattern.object = 0;
419         pattern.object_type = lrdf_literal;
420
421         lrdf_statement* matches = lrdf_matches(&pattern);
422         free(pattern.subject);
423         free(pattern.predicate);
424
425         stringstream object;
426         if (matches != 0){
427                 object << matches->object;
428         }
429
430         lrdf_free_statements(matches);
431         return object.str();
432 }
433
434 void 
435 AudioLibrary::set_field (string uri, string field, string literal)
436 {
437         lrdf_statement pattern;
438
439         pattern.subject = strdup(uri.c_str());
440
441         string local_field = field_uri(field);
442         pattern.predicate = strdup(local_field.c_str());
443
444         pattern.object = 0;
445         pattern.object_type = lrdf_literal;
446
447         lrdf_remove_matches(&pattern);
448         free(pattern.subject);
449         free(pattern.predicate);
450
451         lrdf_add_triple(src.c_str(), uri.c_str(), local_field.c_str(), 
452                         literal.c_str(), lrdf_literal);
453
454          fields_changed(); /* EMIT SIGNAL */
455 }
456
457 string
458 AudioLibrary::field_uri (string name)
459 {
460         stringstream local_field;
461         local_field << "file:sfdb/fields/" << name;
462
463         return local_field.str();
464 }
465
466 string
467 AudioLibrary::get_label (string uri)
468 {
469         lrdf_statement pattern;
470         pattern.subject = strdup(uri.c_str());
471         pattern.predicate = RDFS_BASE "label";
472         pattern.object = 0;
473         pattern.object_type = lrdf_literal;
474
475         lrdf_statement* matches = lrdf_matches (&pattern);
476         free(pattern.subject);
477
478         stringstream label;
479         if (matches != 0){
480                 label << matches->object;
481         }
482
483         lrdf_free_statements(matches);
484
485         return label.str();
486 }
487
488 void
489 AudioLibrary::set_label (string uri, string label)
490 {
491         lrdf_statement pattern;
492         pattern.subject = strdup(uri.c_str());
493         pattern.predicate = RDFS_BASE "label";
494         pattern.object = 0;
495         pattern.object_type = lrdf_literal;
496
497         lrdf_remove_matches(&pattern);
498         free(pattern.subject);
499
500         lrdf_add_triple(src.c_str(), uri.c_str(), RDFS_BASE "label", 
501                         label.c_str(), lrdf_literal);
502 }
503