~ubuntu-branches/ubuntu/utopic/rhythmbox/utopic-proposed

« back to all changes in this revision

Viewing changes to rhythmdb/rhythmdb-tree.c

Tags: upstream-0.9.2
ImportĀ upstreamĀ versionĀ 0.9.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
29
29
#endif
30
30
#include <unistd.h>
31
31
#include <stdlib.h>
 
32
#include <errno.h>
32
33
#include <string.h>
33
34
#include <glib/gprintf.h>
 
35
#include <glib/gatomic.h>
34
36
#include <libgnome/gnome-i18n.h>
35
37
#include <gtk/gtkliststore.h>
36
38
#include <libxml/entities.h>
43
45
#include "rb-debug.h"
44
46
#include "rb-util.h"
45
47
#include "rb-file-helpers.h"
46
 
#include "rb-atomic.h"
47
 
#include "rb-string-helpers.h"
48
 
 
49
 
static void rhythmdb_tree_class_init (RhythmDBTreeClass *klass);
50
 
static void rhythmdb_tree_init (RhythmDBTree *shell_player);
 
48
 
 
49
typedef struct RhythmDBTreeProperty
 
50
{
 
51
#ifndef G_DISABLE_ASSERT
 
52
        guint magic;
 
53
#endif  
 
54
        struct RhythmDBTreeProperty *parent;
 
55
        GHashTable *children;
 
56
} RhythmDBTreeProperty;
 
57
 
 
58
#define RHYTHMDB_TREE_PROPERTY_FROM_ENTRY(entry) ((RhythmDBTreeProperty *) entry->data)
 
59
 
 
60
G_DEFINE_TYPE(RhythmDBTree, rhythmdb_tree, RHYTHMDB_TYPE)
 
61
 
51
62
static void rhythmdb_tree_finalize (GObject *object);
52
63
 
53
64
static void rhythmdb_tree_load (RhythmDB *rdb, gboolean *die);
54
65
static void rhythmdb_tree_save (RhythmDB *rdb);
55
 
static RhythmDBEntry * rhythmdb_tree_entry_new (RhythmDB *db, RhythmDBEntryType type,
56
 
                                                const char *uri);
57
 
static void rhythmdb_tree_entry_set (RhythmDB *db, RhythmDBEntry *entry,
58
 
                                     guint propid, const GValue *value);
 
66
static void rhythmdb_tree_entry_new (RhythmDB *db, RhythmDBEntry *entry);
 
67
static gboolean rhythmdb_tree_entry_set (RhythmDB *db, RhythmDBEntry *entry,
 
68
                                         guint propid, const GValue *value);
59
69
 
60
 
static void rhythmdb_tree_entry_get (RhythmDB *db, RhythmDBEntry *entry,
61
 
                                guint propid, GValue *value);
62
70
static void rhythmdb_tree_entry_delete (RhythmDB *db, RhythmDBEntry *entry);
63
71
static void rhythmdb_tree_entry_delete_by_type (RhythmDB *adb, RhythmDBEntryType type);
64
72
 
65
73
static RhythmDBEntry * rhythmdb_tree_entry_lookup_by_location (RhythmDB *db, const char *uri);
 
74
static void rhythmdb_tree_entry_foreach (RhythmDB *adb, GFunc func, gpointer user_data);
66
75
static void rhythmdb_tree_do_full_query (RhythmDB *db, GPtrArray *query,
67
76
                                         GtkTreeModel *main_model, gboolean *cancel);
68
 
gboolean rhythmdb_tree_evaluate_query (RhythmDB *adb, GPtrArray *query,
 
77
static gboolean rhythmdb_tree_evaluate_query (RhythmDB *adb, GPtrArray *query,
69
78
                                       RhythmDBEntry *aentry);
70
79
 
71
80
typedef void (*RBTreeEntryItFunc)(RhythmDBTree *db, 
72
 
                                  RhythmDBTreeEntry *entry, 
 
81
                                  RhythmDBEntry *entry, 
73
82
                                  gpointer data);
74
83
 
75
84
typedef void (*RBTreePropertyItFunc)(RhythmDBTree *db, 
86
95
 
87
96
#define RHYTHMDB_TREE_XML_VERSION "1.0"
88
97
 
89
 
#define RHYTHMDB_TREE_ENTRY_VALUE(ENTRY, PROPID) (&((ENTRY)->properties[PROPID]))
90
 
#define RHYTHMDB_TREE_ENTRY_GET_TYPE(ENTRY) (g_value_get_int (RHYTHMDB_TREE_ENTRY_VALUE (ENTRY, RHYTHMDB_PROP_TYPE)))
91
 
 
92
 
#ifndef G_DISABLE_ASSERT
93
 
static inline RhythmDBTreeProperty *
94
 
assert_valid_tree_property (RhythmDBTreeProperty *prop)
95
 
{
96
 
        g_assert (((RhythmDBTreeProperty *) prop)->magic == 0xf00dbeef);
97
 
        return prop;
98
 
}
99
 
#define RHYTHMDB_TREE_PROPERTY(x) (assert_valid_tree_property (x))
100
 
#else
101
 
#define RHYTHMDB_TREE_PROPERTY(x) ((RhythmdbTreeProperty *) x)
102
 
#endif
103
 
 
104
 
#ifndef G_DISABLE_ASSERT
105
 
static inline RhythmDBTreeEntry *
106
 
assert_valid_tree_entry (RhythmDBTreeEntry *entry)
107
 
{
108
 
        g_assert (((RhythmDBTreeEntry *) entry)->magic == 0xdeadb33f);
109
 
        return entry;
110
 
}
111
 
#define RHYTHMDB_TREE_ENTRY(x) (assert_valid_tree_entry (x))
112
 
#else
113
 
#define RHYTHMDB_TREE_ENTRY(x) ((RhythmDBEntry *) x)
114
 
#endif
115
 
 
116
 
static RhythmDBEntry *rhythmdb_tree_entry_allocate (RhythmDBTree *db, RhythmDBEntryType type);
117
 
static gboolean rhythmdb_tree_entry_insert (RhythmDBTree *db, RhythmDBTreeEntry *entry,
118
 
                                            RhythmDBEntryType type, const char *uri,
119
 
                                            const char *genrename, const char *artistname,
120
 
                                            const char *albumname);
121
 
 
122
98
static void destroy_tree_property (RhythmDBTreeProperty *prop);
123
99
static RhythmDBTreeProperty *get_or_create_album (RhythmDBTree *db, RhythmDBTreeProperty *artist,
124
 
                                                  const char *name);
 
100
                                                  RBRefString *name);
125
101
static RhythmDBTreeProperty *get_or_create_artist (RhythmDBTree *db, RhythmDBTreeProperty *genre,
126
 
                                                   const char *name);
127
 
static inline RhythmDBTreeProperty *get_or_create_genre (RhythmDBTree *db, RhythmDBEntryType type,
128
 
                                                         const char *name);
129
 
 
130
 
static void rhythmdb_tree_entry_finalize (RhythmDBTreeEntry *entry);
131
 
static void remove_entry_from_album (RhythmDBTree *db, RhythmDBTreeEntry *entry);
132
 
 
133
 
static char *get_entry_genre_name (RhythmDBTreeEntry *entry);
134
 
static char *get_entry_artist_name (RhythmDBTreeEntry *entry);
135
 
static char *get_entry_album_name (RhythmDBTreeEntry *entry);
136
 
static char *get_entry_genre_sort_key (RhythmDBTreeEntry *entry);
137
 
static char *get_entry_artist_sort_key (RhythmDBTreeEntry *entry);
138
 
static char *get_entry_album_sort_key (RhythmDBTreeEntry *entry);
139
 
static char *get_entry_genre_folded (RhythmDBTreeEntry *entry);
140
 
static char *get_entry_artist_folded (RhythmDBTreeEntry *entry);
141
 
static char *get_entry_album_folded (RhythmDBTreeEntry *entry);
142
 
 
143
 
static void handle_genre_deletion (RhythmDBTree *db, const char *name);
144
 
static void handle_artist_deletion (RhythmDBTree *db, const char *name);
145
 
static void handle_album_deletion (RhythmDBTree *db, const char *name);
 
102
                                                   RBRefString *name);
 
103
static RhythmDBTreeProperty *get_or_create_genre (RhythmDBTree *db, RhythmDBEntryType type,
 
104
                                                         RBRefString *name);
 
105
 
 
106
static void remove_entry_from_album (RhythmDBTree *db, RhythmDBEntry *entry);
146
107
 
147
108
static GList *split_query_by_disjunctions (RhythmDBTree *db, GPtrArray *query);
148
109
static gboolean evaluate_conjunctive_subquery (RhythmDBTree *db, GPtrArray *query,
149
 
                                               guint base, guint max, RhythmDBTreeEntry *entry);
 
110
                                               guint base, guint max, RhythmDBEntry *entry);
150
111
 
151
112
struct RhythmDBTreePrivate
152
113
{
153
 
        GMemChunk *entry_memchunk;
154
114
        GMemChunk *property_memchunk;
155
115
 
156
116
        GHashTable *entries;
158
118
        GMutex *genres_lock;
159
119
        gboolean finalizing;
160
120
 
 
121
        guint idle_load_id;
161
122
};
162
123
 
163
124
enum
165
126
        PROP_0,
166
127
};
167
128
 
168
 
static GObjectClass *parent_class = NULL;
169
 
 
170
 
GType
171
 
rhythmdb_tree_get_type (void)
172
 
{
173
 
        static GType rhythmdb_tree_type = 0;
174
 
 
175
 
        if (rhythmdb_tree_type == 0)
176
 
        {
177
 
                static const GTypeInfo our_info =
178
 
                {
179
 
                        sizeof (RhythmDBTreeClass),
180
 
                        NULL,
181
 
                        NULL,
182
 
                        (GClassInitFunc) rhythmdb_tree_class_init,
183
 
                        NULL,
184
 
                        NULL,
185
 
                        sizeof (RhythmDBTree),
186
 
                        0,
187
 
                        (GInstanceInitFunc) rhythmdb_tree_init
188
 
                };
189
 
 
190
 
                rhythmdb_tree_type = g_type_register_static (RHYTHMDB_TYPE,
191
 
                                                             "RhythmDBTree",
192
 
                                                             &our_info, 0);
193
 
        }
194
 
 
195
 
        return rhythmdb_tree_type;
196
 
}
 
129
const int RHYTHMDB_TREE_PARSER_INITIAL_BUFFER_SIZE = 512;
 
130
 
197
131
 
198
132
static void
199
133
rhythmdb_tree_class_init (RhythmDBTreeClass *klass)
201
135
        GObjectClass *object_class = G_OBJECT_CLASS (klass);
202
136
        RhythmDBClass *rhythmdb_class = RHYTHMDB_CLASS (klass);
203
137
 
204
 
        parent_class = g_type_class_peek_parent (klass);
205
 
 
206
138
        object_class->finalize = rhythmdb_tree_finalize;
207
139
 
208
140
        rhythmdb_class->impl_load = rhythmdb_tree_load;
209
141
        rhythmdb_class->impl_save = rhythmdb_tree_save;
210
142
        rhythmdb_class->impl_entry_new = rhythmdb_tree_entry_new;
211
143
        rhythmdb_class->impl_entry_set = rhythmdb_tree_entry_set;
212
 
        rhythmdb_class->impl_entry_get = rhythmdb_tree_entry_get;
213
144
        rhythmdb_class->impl_entry_delete = rhythmdb_tree_entry_delete;
214
145
        rhythmdb_class->impl_entry_delete_by_type = rhythmdb_tree_entry_delete_by_type;
215
146
        rhythmdb_class->impl_lookup_by_location = rhythmdb_tree_entry_lookup_by_location;
 
147
        rhythmdb_class->impl_entry_foreach = rhythmdb_tree_entry_foreach;
216
148
        rhythmdb_class->impl_evaluate_query = rhythmdb_tree_evaluate_query;
217
149
        rhythmdb_class->impl_do_full_query = rhythmdb_tree_do_full_query;
218
150
}
224
156
 
225
157
        db->priv->entries = g_hash_table_new (g_str_hash, g_str_equal);
226
158
 
227
 
        db->priv->entry_memchunk = g_mem_chunk_new ("RhythmDBTree entry memchunk",
228
 
                                                    sizeof (RhythmDBTreeEntry),
229
 
                                                    1024, G_ALLOC_AND_FREE);
230
159
        db->priv->property_memchunk = g_mem_chunk_new ("RhythmDBTree property memchunk",
231
160
                                                       sizeof (RhythmDBTreeProperty),
232
161
                                                       1024, G_ALLOC_AND_FREE);
234
163
                                                  NULL, (GDestroyNotify)g_hash_table_destroy);
235
164
}
236
165
 
237
 
void
238
 
rhythmdb_entry_unref (RhythmDB *adb, RhythmDBEntry *aentry)
239
 
{
240
 
        RhythmDBTree *db = (RhythmDBTree *) adb;
241
 
        RhythmDBTreeEntry *entry = (RhythmDBTreeEntry *) aentry;
242
 
 
243
 
        if (rb_atomic_dec (&entry->refcount) <= 1) {
244
 
                rhythmdb_write_lock (adb);
245
 
                rhythmdb_tree_entry_destroy (db, entry);
246
 
                rhythmdb_write_unlock (adb);
247
 
        }
248
 
}
249
 
 
250
 
void
251
 
rhythmdb_entry_unref_unlocked (RhythmDB *adb, RhythmDBEntry *aentry)
252
 
{
253
 
        RhythmDBTree *db = (RhythmDBTree *) adb;
254
 
        RhythmDBTreeEntry *entry = (RhythmDBTreeEntry *) aentry;
255
 
 
256
 
        if (rb_atomic_dec (&entry->refcount) <= 1) {
257
 
                rhythmdb_tree_entry_destroy (db, entry);
258
 
        }
259
 
}
260
 
 
261
 
static inline char *
262
 
get_entry_genre_name (RhythmDBTreeEntry *entry)
263
 
{
264
 
        return entry->album->parent->parent->name;
265
 
}
266
 
 
267
 
static inline char *
268
 
get_entry_artist_name (RhythmDBTreeEntry *entry)
269
 
{
270
 
        return entry->album->parent->name;
271
 
}
272
 
 
273
 
static inline char *
274
 
get_entry_album_name (RhythmDBTreeEntry *entry)
275
 
{
276
 
        return entry->album->name;
277
 
}
278
 
 
279
 
static inline char *
280
 
get_entry_genre_sort_key (RhythmDBTreeEntry *entry)
281
 
{
282
 
        return entry->album->parent->parent->sort_key;
283
 
}
284
 
 
285
 
static inline char *
286
 
get_entry_artist_sort_key (RhythmDBTreeEntry *entry)
287
 
{
288
 
        return entry->album->parent->sort_key;
289
 
}
290
 
 
291
 
static inline char *
292
 
get_entry_album_sort_key (RhythmDBTreeEntry *entry)
293
 
{
294
 
        return entry->album->sort_key;
295
 
}
296
 
 
297
 
static inline char *
298
 
get_entry_genre_folded (RhythmDBTreeEntry *entry)
299
 
{
300
 
        return entry->album->parent->parent->folded;
301
 
}
302
 
 
303
 
static inline char *
304
 
get_entry_artist_folded (RhythmDBTreeEntry *entry)
305
 
{
306
 
        return entry->album->parent->folded;
307
 
}
308
 
 
309
 
static inline char *
310
 
get_entry_album_folded (RhythmDBTreeEntry *entry)
311
 
{
312
 
        return entry->album->folded;
313
 
}
314
 
 
315
 
static inline void
316
 
sanity_check_entry_tree (RhythmDBTreeEntry *entry)
317
 
{
318
 
#if defined(RHYTHMDB_ENABLE_SANITY_CHECK) && RHYTHMDB_ENABLE_SANITY_CHECK > 1
319
 
        RHYTHMDB_TREE_ENTRY (entry); 
320
 
        RHYTHMDB_TREE_PROPERTY (entry->album); 
321
 
        RHYTHMDB_TREE_PROPERTY (entry->album->parent); 
322
 
        RHYTHMDB_TREE_PROPERTY (entry->album->parent->parent); 
323
 
#endif
324
 
}
325
 
#if defined(RHYTHMDB_ENABLE_SANITY_CHECK) && RHYTHMDB_ENABLE_SANITY_CHECK > 1
326
 
static void
327
 
sanity_check_entry_tree_from_hash (gpointer unused, RhythmDBTreeEntry *entry)
328
 
{
329
 
        sanity_check_entry_tree (entry);
330
 
}
331
 
#endif
332
 
static void
333
 
sanity_check_database (RhythmDBTree *db)
334
 
{
335
 
#if defined(RHYTHMDB_ENABLE_SANITY_CHECK) && RHYTHMDB_ENABLE_SANITY_CHECK > 1
336
 
        g_hash_table_foreach (db->priv->entries, (GHFunc) sanity_check_entry_tree_from_hash, NULL);
337
 
#endif
338
 
}
339
 
 
340
 
static void
341
 
unparent_entries (const char *uri, RhythmDBTreeEntry *entry, RhythmDBTree *db)
 
166
static void
 
167
unparent_entries (const char *uri, RhythmDBEntry *entry, RhythmDBTree *db)
342
168
{
343
169
        remove_entry_from_album (db, entry);
344
 
        rhythmdb_tree_entry_finalize (entry);
345
170
}
346
171
 
347
172
static void
358
183
 
359
184
        db->priv->finalizing = TRUE;
360
185
 
361
 
        sanity_check_database (db);
362
 
        
363
186
        g_hash_table_foreach (db->priv->entries, (GHFunc) unparent_entries, db);
364
187
 
365
188
        g_hash_table_destroy (db->priv->entries);
366
189
 
367
 
        g_mem_chunk_destroy (db->priv->entry_memchunk);
368
190
        g_mem_chunk_destroy (db->priv->property_memchunk);
369
191
 
370
192
        g_hash_table_destroy (db->priv->genres);
371
193
 
372
194
        g_free (db->priv);
373
195
 
374
 
        G_OBJECT_CLASS (parent_class)->finalize (object);
 
196
        G_OBJECT_CLASS (rhythmdb_tree_parent_class)->finalize (object);
375
197
}
376
198
 
377
199
struct RhythmDBTreeLoadContext
387
209
                RHYTHMDB_TREE_PARSER_STATE_END,
388
210
        } state;
389
211
        gboolean in_unknown_elt;
390
 
        RhythmDBTreeEntry *entry;
391
 
        char *genrename;
392
 
        char *albumname;
393
 
        char *artistname;
 
212
        RhythmDBEntry *entry;
394
213
        GString *buf;
395
214
        RhythmDBPropType propid;
 
215
 
 
216
        gboolean has_date;
396
217
};
397
218
 
398
219
static void
429
250
                                                type = RHYTHMDB_ENTRY_TYPE_SONG;
430
251
                                        else if (!strcmp (typename, "iradio"))
431
252
                                                type = RHYTHMDB_ENTRY_TYPE_IRADIO_STATION;
 
253
                                        else if (!strcmp (typename, "podcast-post"))
 
254
                                                type = RHYTHMDB_ENTRY_TYPE_PODCAST_POST;
 
255
                                        else if (!strcmp (typename, "podcast-feed"))
 
256
                                                type = RHYTHMDB_ENTRY_TYPE_PODCAST_FEED;
432
257
                                        else
433
258
                                                return;
434
259
                                        type_set = TRUE;
437
262
                        }
438
263
                        g_assert (type_set);
439
264
                        ctx->state = RHYTHMDB_TREE_PARSER_STATE_ENTRY;
440
 
                        ctx->entry = rhythmdb_tree_entry_allocate (ctx->db, type);
441
 
                        ctx->genrename = NULL;
442
 
                        ctx->albumname = NULL;
443
 
                        ctx->artistname = NULL;
 
265
                        ctx->entry = rhythmdb_entry_allocate (RHYTHMDB (ctx->db), type);
 
266
                        ctx->has_date = FALSE;
444
267
                } else
445
268
                        ctx->in_unknown_elt = TRUE;
446
269
                break;
447
270
        }
448
271
        case RHYTHMDB_TREE_PARSER_STATE_ENTRY:
449
272
        {
450
 
                int val = rhythmdb_propid_from_nice_elt_name (RHYTHMDB (ctx->db), name);
 
273
                int val = rhythmdb_propid_from_nice_elt_name (RHYTHMDB (ctx->db), BAD_CAST name);
451
274
                if (val < 0) {
452
275
                        ctx->in_unknown_elt = TRUE;
453
276
                        break;
455
278
                
456
279
                ctx->state = RHYTHMDB_TREE_PARSER_STATE_ENTRY_PROPERTY;
457
280
                ctx->propid = val;
458
 
                ctx->buf = g_string_new ("");
 
281
                g_string_truncate (ctx->buf, 0);
459
282
                break;
460
283
        }
461
284
        case RHYTHMDB_TREE_PARSER_STATE_ENTRY_PROPERTY:
464
287
        }
465
288
}
466
289
 
 
290
static gulong
 
291
parse_ulong (const char *buffer)
 
292
{
 
293
        guint64 val;
 
294
 
 
295
        val = g_ascii_strtoull (buffer, NULL, 10);
 
296
        if (val == G_MAXUINT64)
 
297
                return 0;
 
298
        else
 
299
                return MIN (val, G_MAXUINT32);
 
300
}
 
301
 
467
302
static void
468
303
rhythmdb_tree_parser_end_element (struct RhythmDBTreeLoadContext *ctx, const char *name)
469
304
{
484
319
                break;
485
320
        case RHYTHMDB_TREE_PARSER_STATE_ENTRY:
486
321
        {
487
 
                RhythmDBEntryType type;
488
 
                const char *uri;
489
 
 
490
 
                type = RHYTHMDB_TREE_ENTRY_GET_TYPE (ctx->entry);
491
 
 
492
 
                uri = g_value_get_string (RHYTHMDB_TREE_ENTRY_VALUE (ctx->entry,
493
 
                                                                     RHYTHMDB_PROP_LOCATION));
494
 
                rhythmdb_write_lock ((RhythmDB *) ctx->db);
495
 
                /* Ignore duplicate entries */
496
 
                if (rhythmdb_tree_entry_lookup_by_location ((RhythmDB *) ctx->db, uri) == NULL) {
497
 
                        rhythmdb_tree_entry_insert (ctx->db, ctx->entry, type,
498
 
                                                    uri, ctx->genrename, ctx->artistname,
499
 
                                                    ctx->albumname);
500
 
                        rhythmdb_emit_entry_restored ((RhythmDB *) ctx->db, ctx->entry);
 
322
                if (!ctx->has_date) {
 
323
                        /* there is no date metadata, so this is from an old version
 
324
                         * reset the last-modified timestamp, so that the file is re-read
 
325
                         */
 
326
                        rb_debug ("pre-Date entry found, causing re-read");
 
327
                        ctx->entry->mtime = 0;
501
328
                }
502
 
                
503
 
                g_free (ctx->genrename);
504
 
                g_free (ctx->artistname);
505
 
                g_free (ctx->albumname);
506
 
 
507
 
                rhythmdb_write_unlock (RHYTHMDB (ctx->db));
508
 
 
 
329
                rhythmdb_tree_entry_new (RHYTHMDB (ctx->db), ctx->entry);
 
330
                rhythmdb_entry_insert (RHYTHMDB (ctx->db), ctx->entry);
 
331
                rhythmdb_commit (RHYTHMDB (ctx->db));
509
332
                ctx->state = RHYTHMDB_TREE_PARSER_STATE_RHYTHMDB;
510
333
                break;
511
334
        }
512
335
        case RHYTHMDB_TREE_PARSER_STATE_ENTRY_PROPERTY:
513
336
        {
514
 
                GValue *value = RHYTHMDB_TREE_ENTRY_VALUE (ctx->entry, ctx->propid);
515
 
 
516
337
                /* Handle indexed properties. */
517
338
                switch (ctx->propid)
518
339
                {
 
340
                case RHYTHMDB_PROP_TYPE:
 
341
                        g_assert_not_reached ();
 
342
                        break;
 
343
                case RHYTHMDB_PROP_TITLE:
 
344
                        ctx->entry->title = rb_refstring_new (ctx->buf->str);
 
345
                        break;
519
346
                case RHYTHMDB_PROP_GENRE:
520
 
                        ctx->genrename = ctx->buf->str;
521
 
                        g_string_free (ctx->buf, FALSE);
522
 
                        goto nextstate;
 
347
                        ctx->entry->genre = rb_refstring_new (ctx->buf->str);
 
348
                        break;
523
349
                case RHYTHMDB_PROP_ARTIST:
524
 
                        ctx->artistname = ctx->buf->str;
525
 
                        g_string_free (ctx->buf, FALSE);
526
 
                        goto nextstate;
 
350
                        ctx->entry->artist = rb_refstring_new (ctx->buf->str);
 
351
                        break;
527
352
                case RHYTHMDB_PROP_ALBUM:
528
 
                        ctx->albumname = ctx->buf->str;
529
 
                        g_string_free (ctx->buf, FALSE);
530
 
                        goto nextstate;
531
 
                default:
532
 
                        break;
533
 
                }
534
 
                        
535
 
                /* Other properties */
536
 
                g_value_reset (value);
537
 
 
538
 
                /* Optimization possibility - don't use strtoull? We don't need
539
 
                 * 64 bits. */
540
 
                switch (G_VALUE_TYPE (value))
 
353
                        ctx->entry->album = rb_refstring_new (ctx->buf->str);
 
354
                        break;
 
355
                case RHYTHMDB_PROP_TRACK_NUMBER:
 
356
                        ctx->entry->tracknum = parse_ulong (ctx->buf->str);
 
357
                        break;
 
358
                case RHYTHMDB_PROP_DISC_NUMBER:
 
359
                        ctx->entry->discnum = parse_ulong (ctx->buf->str);
 
360
                        break;
 
361
                case RHYTHMDB_PROP_DATE:
541
362
                {
542
 
                case G_TYPE_STRING:
543
 
                        g_value_set_string_take_ownership (value, ctx->buf->str);
544
 
                        g_string_free (ctx->buf, FALSE);
545
 
                        break;
546
 
                case G_TYPE_BOOLEAN:
547
 
                        g_value_set_boolean (value, g_ascii_strtoull (ctx->buf->str, NULL, 10));
548
 
                        g_string_free (ctx->buf, TRUE);
549
 
                        break;
550
 
                case G_TYPE_INT:
551
 
                        g_value_set_int (value, g_ascii_strtoull (ctx->buf->str, NULL, 10));
552
 
                        g_string_free (ctx->buf, TRUE);
553
 
                        break;
554
 
                case G_TYPE_LONG:
555
 
                        g_value_set_long (value, g_ascii_strtoull (ctx->buf->str, NULL, 10));
556
 
                        g_string_free (ctx->buf, TRUE);
557
 
                        break;
558
 
                case G_TYPE_UINT64:
559
 
                        g_value_set_uint64 (value, g_ascii_strtoull (ctx->buf->str, NULL, 10));
560
 
                        g_string_free (ctx->buf, TRUE);
561
 
                        break;
562
 
                case G_TYPE_FLOAT:
563
 
                        g_value_set_float (value, g_ascii_strtod (ctx->buf->str, NULL));
564
 
                        g_string_free (ctx->buf, TRUE);
565
 
                        break;
566
 
                case G_TYPE_DOUBLE:
567
 
                        g_value_set_double (value, g_ascii_strtod (ctx->buf->str, NULL));
568
 
                        g_string_free (ctx->buf, TRUE);
569
 
                        break;
570
 
                default:
 
363
                        gulong value = parse_ulong (ctx->buf->str);
 
364
                        
 
365
                        if (value > 0)
 
366
                                ctx->entry->date = g_date_new_julian (value);
 
367
                        else
 
368
                                ;
 
369
                        ctx->has_date = TRUE;
 
370
                        break;
 
371
                }
 
372
                case RHYTHMDB_PROP_DURATION:
 
373
                        ctx->entry->duration = parse_ulong (ctx->buf->str);
 
374
                        break;
 
375
                case RHYTHMDB_PROP_FILE_SIZE:
 
376
                        ctx->entry->file_size = parse_ulong (ctx->buf->str);
 
377
                        break;
 
378
                case RHYTHMDB_PROP_LOCATION:
 
379
                        ctx->entry->location = g_strdup (ctx->buf->str);
 
380
                        break;
 
381
                case RHYTHMDB_PROP_MOUNTPOINT:
 
382
                        /* remove this from old podcast-post entries */
 
383
                        if (!g_str_has_prefix (ctx->buf->str, "http://"))
 
384
                                ctx->entry->mountpoint = rb_refstring_new (ctx->buf->str);
 
385
                        break;
 
386
                case RHYTHMDB_PROP_MTIME:
 
387
                        ctx->entry->mtime = parse_ulong (ctx->buf->str);
 
388
                        break;
 
389
                case RHYTHMDB_PROP_FIRST_SEEN:
 
390
                        ctx->entry->first_seen = parse_ulong (ctx->buf->str);
 
391
                        break;
 
392
                case RHYTHMDB_PROP_LAST_SEEN:
 
393
                        ctx->entry->last_seen = parse_ulong (ctx->buf->str);
 
394
                        break;
 
395
                case RHYTHMDB_PROP_RATING:
 
396
                        ctx->entry->rating = g_ascii_strtod (ctx->buf->str, NULL);
 
397
                        break;
 
398
                case RHYTHMDB_PROP_PLAY_COUNT:
 
399
                        ctx->entry->play_count = parse_ulong (ctx->buf->str);
 
400
                        break;
 
401
                case RHYTHMDB_PROP_LAST_PLAYED:
 
402
                        ctx->entry->last_played = parse_ulong (ctx->buf->str);
 
403
                        break;
 
404
                case RHYTHMDB_PROP_BITRATE:
 
405
                        ctx->entry->bitrate = parse_ulong (ctx->buf->str);
 
406
                        break;
 
407
                case RHYTHMDB_PROP_TRACK_GAIN:
 
408
                        ctx->entry->track_gain = g_ascii_strtod (ctx->buf->str, NULL);
 
409
                        break;
 
410
                case RHYTHMDB_PROP_TRACK_PEAK:
 
411
                        ctx->entry->track_peak = g_ascii_strtod (ctx->buf->str, NULL);
 
412
                        break;
 
413
                case RHYTHMDB_PROP_ALBUM_GAIN:
 
414
                        ctx->entry->album_gain = g_ascii_strtod (ctx->buf->str, NULL);
 
415
                        break;
 
416
                case RHYTHMDB_PROP_ALBUM_PEAK:
 
417
                        ctx->entry->album_peak = g_ascii_strtod (ctx->buf->str, NULL);
 
418
                        break;
 
419
                case RHYTHMDB_PROP_MIMETYPE:
 
420
                        ctx->entry->mimetype = rb_refstring_new (ctx->buf->str);
 
421
                        break;
 
422
                case RHYTHMDB_PROP_STATUS:
 
423
                        ctx->entry->podcast->status = parse_ulong (ctx->buf->str);
 
424
                        break;                  
 
425
                case RHYTHMDB_PROP_DESCRIPTION:
 
426
                        ctx->entry->podcast->description = rb_refstring_new (ctx->buf->str);
 
427
                        break;
 
428
                case RHYTHMDB_PROP_SUBTITLE:
 
429
                        ctx->entry->podcast->subtitle = rb_refstring_new (ctx->buf->str);
 
430
                        break;
 
431
                case RHYTHMDB_PROP_SUMMARY:
 
432
                        ctx->entry->podcast->summary = rb_refstring_new (ctx->buf->str);
 
433
                        break;
 
434
                case RHYTHMDB_PROP_LANG:
 
435
                        ctx->entry->podcast->lang = rb_refstring_new (ctx->buf->str);
 
436
                        break;
 
437
                case RHYTHMDB_PROP_COPYRIGHT:
 
438
                        ctx->entry->podcast->copyright = rb_refstring_new (ctx->buf->str);
 
439
                        break;
 
440
                case RHYTHMDB_PROP_IMAGE:
 
441
                        ctx->entry->podcast->image = rb_refstring_new (ctx->buf->str);
 
442
                        break;
 
443
                case RHYTHMDB_PROP_POST_TIME:                   
 
444
                        ctx->entry->podcast->post_time = parse_ulong (ctx->buf->str);
 
445
                        break;
 
446
                case RHYTHMDB_PROP_TITLE_SORT_KEY:
 
447
                case RHYTHMDB_PROP_GENRE_SORT_KEY:
 
448
                case RHYTHMDB_PROP_ARTIST_SORT_KEY:
 
449
                case RHYTHMDB_PROP_ALBUM_SORT_KEY:
 
450
                case RHYTHMDB_PROP_TITLE_FOLDED:
 
451
                case RHYTHMDB_PROP_GENRE_FOLDED:
 
452
                case RHYTHMDB_PROP_ARTIST_FOLDED:
 
453
                case RHYTHMDB_PROP_ALBUM_FOLDED:
 
454
                case RHYTHMDB_PROP_LAST_PLAYED_STR:
 
455
                case RHYTHMDB_PROP_HIDDEN:
 
456
                case RHYTHMDB_PROP_PLAYBACK_ERROR:
 
457
                case RHYTHMDB_PROP_FIRST_SEEN_STR:
 
458
                case RHYTHMDB_PROP_SEARCH_MATCH:
 
459
                case RHYTHMDB_NUM_PROPERTIES:
571
460
                        g_assert_not_reached ();
572
461
                        break;
573
462
                }
574
 
                rhythmdb_entry_sync_mirrored (RHYTHMDB (ctx->db),
575
 
                                              ctx->entry, ctx->propid, value);
 
463
                        
 
464
                rhythmdb_entry_sync_mirrored (RHYTHMDB (ctx->db), ctx->entry, ctx->propid);
576
465
 
577
 
        nextstate:
578
466
                ctx->state = RHYTHMDB_TREE_PARSER_STATE_ENTRY;
579
467
                break;
580
468
        }
622
510
        ctx->state = RHYTHMDB_TREE_PARSER_STATE_START;
623
511
        ctx->db = db;
624
512
        ctx->die = die;
 
513
        ctx->buf = g_string_sized_new (RHYTHMDB_TREE_PARSER_INITIAL_BUFFER_SIZE);
625
514
 
626
515
        g_object_get (G_OBJECT (db), "name", &name, NULL);
627
516
 
628
 
        if (rb_uri_exists (name)) {
 
517
        if (g_file_test (name, G_FILE_TEST_EXISTS)) {
629
518
                ctxt = xmlCreateFileParserCtxt (name);
630
519
                ctx->xmlctx = ctxt;
631
520
                xmlFree (ctxt->sax);
636
525
                xmlFreeParserCtxt (ctxt);
637
526
                        
638
527
        }
 
528
        g_string_free (ctx->buf, TRUE);
639
529
        g_free (name);
640
530
        g_free (sax_handler);
641
531
        g_free (ctx);
645
535
{
646
536
        RhythmDBTree *db;
647
537
        FILE *handle;
 
538
        char *error;
648
539
};
649
540
 
 
541
#ifdef HAVE_GNU_FWRITE_UNLOCKED
 
542
#define RHYTHMDB_FWRITE_REAL fwrite_unlocked
 
543
#define RHYTHMDB_FPUTC_REAL fputc_unlocked
 
544
#else
 
545
#define RHYTHMDB_FWRITE_REAL fwrite
 
546
#define RHYTHMDB_FPUTC_REAL fputc
 
547
#endif
 
548
 
 
549
#define RHYTHMDB_FWRITE(w,x,len,handle,error) do {                      \
 
550
        if (error == NULL) {                                            \
 
551
                if (RHYTHMDB_FWRITE_REAL (w,x,len,handle) != len) {     \
 
552
                        error = g_strdup (g_strerror (errno));          \
 
553
                }                                                       \
 
554
        }                                                               \
 
555
} while (0)
 
556
 
 
557
#define RHYTHMDB_FPUTC(x,handle,error) do {                             \
 
558
        if (error == NULL) {                                            \
 
559
                if (RHYTHMDB_FPUTC_REAL (x,handle) == EOF) {            \
 
560
                        error = g_strdup (g_strerror (errno));          \
 
561
                }                                                       \
 
562
        }                                                               \
 
563
} while (0)
 
564
 
 
565
#define RHYTHMDB_FWRITE_STATICSTR(STR, HANDLE, ERROR) RHYTHMDB_FWRITE(STR, 1, sizeof(STR)-1, HANDLE, ERROR)
 
566
 
 
567
static void
 
568
write_elt_name_open (struct RhythmDBTreeSaveContext *ctx, const xmlChar *elt_name)
 
569
{
 
570
        RHYTHMDB_FWRITE_STATICSTR ("    <", ctx->handle, ctx->error);
 
571
        RHYTHMDB_FWRITE (elt_name, 1, xmlStrlen (elt_name), ctx->handle, ctx->error);
 
572
        RHYTHMDB_FPUTC ('>', ctx->handle, ctx->error);
 
573
}
 
574
 
 
575
static void
 
576
write_elt_name_close (struct RhythmDBTreeSaveContext *ctx, const xmlChar *elt_name)
 
577
{
 
578
        RHYTHMDB_FWRITE_STATICSTR ("</", ctx->handle, ctx->error);
 
579
        RHYTHMDB_FWRITE (elt_name, 1, xmlStrlen (elt_name), ctx->handle, ctx->error);
 
580
        RHYTHMDB_FWRITE_STATICSTR (">\n", ctx->handle, ctx->error);
 
581
}
 
582
 
 
583
static void
 
584
save_entry_string (struct RhythmDBTreeSaveContext *ctx,
 
585
                   const xmlChar *elt_name, const char *str)
 
586
{
 
587
        xmlChar *encoded;
 
588
 
 
589
        g_return_if_fail (str != NULL);
 
590
        write_elt_name_open (ctx, elt_name);
 
591
        encoded = xmlEncodeEntitiesReentrant (NULL, BAD_CAST str);
 
592
        RHYTHMDB_FWRITE (encoded, 1, xmlStrlen (encoded), ctx->handle, ctx->error);
 
593
        g_free (encoded);
 
594
        write_elt_name_close (ctx, elt_name);
 
595
}
 
596
 
 
597
static void
 
598
save_entry_int (struct RhythmDBTreeSaveContext *ctx,
 
599
                const xmlChar *elt_name, int num)
 
600
{
 
601
        char buf[92];
 
602
        if (num == 0)
 
603
                return;
 
604
        write_elt_name_open (ctx, elt_name);
 
605
        g_snprintf (buf, sizeof (buf), "%d", num);
 
606
        RHYTHMDB_FWRITE (buf, 1, strlen (buf), ctx->handle, ctx->error);
 
607
        write_elt_name_close (ctx, elt_name);
 
608
}
 
609
 
 
610
static void
 
611
save_entry_ulong (struct RhythmDBTreeSaveContext *ctx,
 
612
                  const xmlChar *elt_name, gulong num, gboolean save_zeroes)
 
613
{
 
614
        char buf[92];
 
615
        if (num == 0 && !save_zeroes)
 
616
                return;
 
617
        write_elt_name_open (ctx, elt_name);
 
618
        g_snprintf (buf, sizeof (buf), "%lu", num);
 
619
        RHYTHMDB_FWRITE (buf, 1, strlen (buf), ctx->handle, ctx->error);
 
620
        write_elt_name_close (ctx, elt_name);
 
621
}
 
622
 
 
623
static void
 
624
save_entry_uint64 (struct RhythmDBTreeSaveContext *ctx, const xmlChar *elt_name,
 
625
                   guint64 num)
 
626
{
 
627
        char buf[92];
 
628
 
 
629
        if (num == 0)
 
630
                return;
 
631
 
 
632
        write_elt_name_open (ctx, elt_name);
 
633
        g_snprintf (buf, sizeof (buf), "%" G_GUINT64_FORMAT, num);
 
634
        RHYTHMDB_FWRITE (buf, 1, strlen (buf), ctx->handle, ctx->error);
 
635
        write_elt_name_close (ctx, elt_name);
 
636
}
 
637
 
 
638
static void
 
639
save_entry_double (struct RhythmDBTreeSaveContext *ctx,
 
640
                   const xmlChar *elt_name, double num)
 
641
{
 
642
        char buf[92];
 
643
 
 
644
        if (num > -0.001 && num < 0.001)
 
645
                return;
 
646
 
 
647
        write_elt_name_open (ctx, elt_name);
 
648
        g_snprintf (buf, sizeof (buf), "%f", num);
 
649
        RHYTHMDB_FWRITE (buf, 1, strlen (buf), ctx->handle, ctx->error);
 
650
        write_elt_name_close (ctx, elt_name);
 
651
}
 
652
 
650
653
/* This code is intended to be highly optimized.  This came at a small
651
654
 * readability cost.  Sorry about that.
652
655
 */
653
656
static void
654
 
save_entry (RhythmDBTree *db, RhythmDBTreeEntry *entry, struct RhythmDBTreeSaveContext *ctx)
 
657
save_entry (RhythmDBTree *db, RhythmDBEntry *entry, struct RhythmDBTreeSaveContext *ctx)
655
658
{
656
 
#ifdef HAVE_GNU_FWRITE_UNLOCKED
657
 
#define RHYTHMDB_FWRITE fwrite_unlocked
658
 
#define RHYTHMDB_FPUTC fputc_unlocked
659
 
#else
660
 
#define RHYTHMDB_FWRITE fwrite
661
 
#define RHYTHMDB_FPUTC fputc
662
 
#endif
663
 
        
664
 
#define RHYTHMDB_FWRITE_STATICSTR(STR, F) RHYTHMDB_FWRITE (STR, 1, sizeof (STR)-1, F)
665
 
#define RHYTHMDB_FWRITE_SMALLTYPE(F, FMT, TYPE)                         \
666
 
G_STMT_START {                                                          \
667
 
        g_snprintf (small_buf, sizeof (small_buf),                      \
668
 
                    FMT, g_value_get_ ## TYPE (value));                 \
669
 
        RHYTHMDB_FWRITE (small_buf, 1, strlen (small_buf), F);          \
670
 
} G_STMT_END
671
 
#define RHYTHMDB_FWRITE_ENCODED_STR(STR, F)                             \
672
 
G_STMT_START {                                                          \
673
 
char *encoded;                                                          \
674
 
encoded = xmlEncodeEntitiesReentrant (NULL, STR);                       \
675
 
RHYTHMDB_FWRITE (encoded, 1, strlen (encoded), F);                      \
676
 
g_free (encoded);                                                       \
677
 
} G_STMT_END
678
 
        
679
659
        RhythmDBPropType i;
680
 
        
681
 
        RHYTHMDB_FWRITE_STATICSTR ("  <entry type=\"", ctx->handle);
682
 
        if (RHYTHMDB_TREE_ENTRY_GET_TYPE (entry) == RHYTHMDB_ENTRY_TYPE_SONG) {
683
 
                RHYTHMDB_FWRITE_STATICSTR ("song", ctx->handle);
684
 
        } else if (RHYTHMDB_TREE_ENTRY_GET_TYPE (entry) == RHYTHMDB_ENTRY_TYPE_IRADIO_STATION) {
685
 
                RHYTHMDB_FWRITE_STATICSTR ("iradio", ctx->handle);
686
 
        }
687
 
        RHYTHMDB_FWRITE_STATICSTR ("\">\n", ctx->handle);
 
660
 
 
661
        if (ctx->error)
 
662
                return;
 
663
 
 
664
        RHYTHMDB_FWRITE_STATICSTR ("  <entry type=\"", ctx->handle, ctx->error);
 
665
 
 
666
        if (entry->type == RHYTHMDB_ENTRY_TYPE_SONG) {
 
667
                RHYTHMDB_FWRITE_STATICSTR ("song", ctx->handle, ctx->error);
 
668
        } else if (entry->type == RHYTHMDB_ENTRY_TYPE_IRADIO_STATION) {
 
669
                RHYTHMDB_FWRITE_STATICSTR ("iradio", ctx->handle, ctx->error);
 
670
        } else if (entry->type == RHYTHMDB_ENTRY_TYPE_PODCAST_POST) {
 
671
                RHYTHMDB_FWRITE_STATICSTR ("podcast-post", ctx->handle, ctx->error);
 
672
        } else if (entry->type == RHYTHMDB_ENTRY_TYPE_PODCAST_FEED) {
 
673
                RHYTHMDB_FWRITE_STATICSTR ("podcast-feed", ctx->handle, ctx->error);
 
674
        } else
 
675
                g_assert_not_reached ();
 
676
 
 
677
        RHYTHMDB_FWRITE_STATICSTR ("\">\n", ctx->handle, ctx->error);
688
678
                
689
679
        /* Skip over the first property - the type */
690
 
        for (i = 1; i < RHYTHMDB_NUM_SAVED_PROPERTIES; i++) {
691
 
                const char *elt_name;
692
 
                GValue *value;
693
 
                char small_buf[92];
694
 
 
695
 
                value = RHYTHMDB_TREE_ENTRY_VALUE (entry, i);
696
 
 
697
 
                /* Optimization - don't save default values */
698
 
                switch (G_VALUE_TYPE (value)) {
699
 
                case G_TYPE_INT:
700
 
                        if (g_value_get_int (value) == 0)
701
 
                                continue;
702
 
                        break;
703
 
                case G_TYPE_LONG:
704
 
                        if (g_value_get_long (value) == 0)
705
 
                                continue;
706
 
                        break;
707
 
                case G_TYPE_FLOAT:
708
 
                {
709
 
                        float v = g_value_get_float (value);
710
 
                        /* We don't care about precision so much. */
711
 
                        if (v > -0.001 && v < 0.001)
712
 
                                continue;
713
 
                        break;
714
 
                }
715
 
                case G_TYPE_DOUBLE:
716
 
                {
717
 
                        double v = g_value_get_double (value);
718
 
                        if (v > -0.001 && v < 0.001)
719
 
                                continue;
720
 
                        break;
721
 
                }
722
 
                case G_TYPE_BOOLEAN:
723
 
                        if (g_value_get_boolean (value) == FALSE)
724
 
                                continue;
725
 
                        break;
726
 
                }
727
 
 
 
680
        for (i = 1; i < RHYTHMDB_NUM_PROPERTIES; i++) {
 
681
                const xmlChar *elt_name;
 
682
 
 
683
                if (ctx->error)
 
684
                        return;
 
685
                
728
686
                elt_name = rhythmdb_nice_elt_name_from_propid ((RhythmDB *) ctx->db, i);
729
687
 
730
 
                RHYTHMDB_FWRITE_STATICSTR ("    <", ctx->handle);
731
 
                RHYTHMDB_FWRITE (elt_name, 1, strlen (elt_name), ctx->handle);
732
 
                RHYTHMDB_FPUTC ('>', ctx->handle);
733
 
                    
734
 
                /* Handle special properties. */
735
688
                switch (i)
736
689
                {
 
690
                case RHYTHMDB_PROP_TYPE:
 
691
                        break;
 
692
                case RHYTHMDB_PROP_TITLE:
 
693
                        save_entry_string(ctx, elt_name, rb_refstring_get (entry->title));
 
694
                        break;
737
695
                case RHYTHMDB_PROP_ALBUM:
738
 
                        RHYTHMDB_FWRITE_ENCODED_STR(get_entry_album_name (entry), ctx->handle);
739
 
                        goto finish_elt;
 
696
                        save_entry_string(ctx, elt_name, rb_refstring_get (entry->album));
 
697
                        break;
740
698
                case RHYTHMDB_PROP_ARTIST:
741
 
                        RHYTHMDB_FWRITE_ENCODED_STR(get_entry_artist_name (entry), ctx->handle);
742
 
                        goto finish_elt;
 
699
                        save_entry_string(ctx, elt_name, rb_refstring_get (entry->artist));
 
700
                        break;
743
701
                case RHYTHMDB_PROP_GENRE:
744
 
                        RHYTHMDB_FWRITE_ENCODED_STR(get_entry_genre_name (entry), ctx->handle);
745
 
                        goto finish_elt;
746
 
                default:
747
 
                        break;
748
 
                }
749
 
 
750
 
                switch (G_VALUE_TYPE (value))
751
 
                {
752
 
                case G_TYPE_STRING:
753
 
                        RHYTHMDB_FWRITE_ENCODED_STR(g_value_get_string (value), ctx->handle);
754
 
                        break;
755
 
                case G_TYPE_BOOLEAN:
756
 
                        RHYTHMDB_FWRITE_SMALLTYPE (ctx->handle, "%d", boolean);
757
 
                        break;
758
 
                case G_TYPE_INT:
759
 
                        RHYTHMDB_FWRITE_SMALLTYPE (ctx->handle, "%d", int);
760
 
                        break;
761
 
                case G_TYPE_LONG:
762
 
                        RHYTHMDB_FWRITE_SMALLTYPE (ctx->handle, "%ld", long);
763
 
                        break;
764
 
                case G_TYPE_UINT64:
765
 
                        RHYTHMDB_FWRITE_SMALLTYPE (ctx->handle, "%llu", uint64);
766
 
                        break;
767
 
                case G_TYPE_FLOAT:
768
 
                        RHYTHMDB_FWRITE_SMALLTYPE (ctx->handle, "%f", float);
769
 
                        break;
770
 
                case G_TYPE_DOUBLE:
771
 
                        RHYTHMDB_FWRITE_SMALLTYPE (ctx->handle, "%f", double);
772
 
                        break;
773
 
                default:
774
 
                        g_assert_not_reached ();
775
 
                        break;
776
 
                }
777
 
 
778
 
        finish_elt:
779
 
                RHYTHMDB_FWRITE_STATICSTR ("</", ctx->handle);
780
 
                RHYTHMDB_FWRITE (elt_name, 1, strlen (elt_name), ctx->handle);
781
 
                RHYTHMDB_FWRITE_STATICSTR (">\n", ctx->handle);
 
702
                        save_entry_string(ctx, elt_name, rb_refstring_get (entry->genre));
 
703
                        break;
 
704
                case RHYTHMDB_PROP_TRACK_NUMBER:
 
705
                        save_entry_ulong (ctx, elt_name, entry->tracknum, FALSE);
 
706
                        break;
 
707
                case RHYTHMDB_PROP_DISC_NUMBER:
 
708
                        save_entry_ulong (ctx, elt_name, entry->discnum, FALSE);
 
709
                        break;
 
710
                case RHYTHMDB_PROP_DATE:
 
711
                        if (entry->date)
 
712
                                save_entry_ulong (ctx, elt_name, g_date_get_julian (entry->date), TRUE);
 
713
                        else
 
714
                                save_entry_ulong (ctx, elt_name, 0, TRUE);
 
715
                        break;
 
716
                case RHYTHMDB_PROP_DURATION:
 
717
                        save_entry_ulong (ctx, elt_name, entry->duration, FALSE);
 
718
                        break;
 
719
                case RHYTHMDB_PROP_BITRATE:
 
720
                        save_entry_int(ctx, elt_name, entry->bitrate);
 
721
                        break;
 
722
                case RHYTHMDB_PROP_TRACK_GAIN:
 
723
                        save_entry_double(ctx, elt_name, entry->track_gain);
 
724
                        break;
 
725
                case RHYTHMDB_PROP_TRACK_PEAK:
 
726
                        save_entry_double(ctx, elt_name, entry->track_peak);
 
727
                        break;
 
728
                case RHYTHMDB_PROP_ALBUM_GAIN:
 
729
                        save_entry_double(ctx, elt_name, entry->album_gain);
 
730
                        break;
 
731
                case RHYTHMDB_PROP_ALBUM_PEAK:
 
732
                        save_entry_double(ctx, elt_name, entry->album_peak);
 
733
                        break;
 
734
                case RHYTHMDB_PROP_LOCATION:
 
735
                        save_entry_string(ctx, elt_name, entry->location);
 
736
                        break;
 
737
                case RHYTHMDB_PROP_MOUNTPOINT:
 
738
                        /* Avoid crashes on exit when upgrading from 0.8
 
739
                         * and no mountpoint is available from some entries */
 
740
                        if (entry->mountpoint) {
 
741
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->mountpoint));
 
742
                        }
 
743
                        break;
 
744
                case RHYTHMDB_PROP_FILE_SIZE:
 
745
                        save_entry_uint64(ctx, elt_name, entry->file_size);
 
746
                        break;
 
747
                case RHYTHMDB_PROP_MIMETYPE:
 
748
                        save_entry_string(ctx, elt_name, rb_refstring_get (entry->mimetype));
 
749
                        break;
 
750
                case RHYTHMDB_PROP_MTIME:
 
751
                        save_entry_ulong (ctx, elt_name, entry->mtime, FALSE);
 
752
                        break;
 
753
                case RHYTHMDB_PROP_FIRST_SEEN:
 
754
                        save_entry_ulong (ctx, elt_name, entry->first_seen, FALSE);
 
755
                        break;
 
756
                case RHYTHMDB_PROP_LAST_SEEN:
 
757
                        save_entry_ulong (ctx, elt_name, entry->last_seen, FALSE);
 
758
                        break;
 
759
                case RHYTHMDB_PROP_RATING:
 
760
                        save_entry_double(ctx, elt_name, entry->rating);
 
761
                        break;
 
762
                case RHYTHMDB_PROP_PLAY_COUNT:
 
763
                        save_entry_ulong (ctx, elt_name, entry->play_count, FALSE);
 
764
                        break;
 
765
                case RHYTHMDB_PROP_LAST_PLAYED:
 
766
                        save_entry_ulong (ctx, elt_name, entry->last_played, FALSE);
 
767
                        break;
 
768
                case RHYTHMDB_PROP_STATUS:
 
769
                        if (entry->podcast)
 
770
                                save_entry_ulong (ctx, elt_name, entry->podcast->status, FALSE);
 
771
                        break;
 
772
                case RHYTHMDB_PROP_DESCRIPTION:
 
773
                        if (entry->podcast && entry->podcast->description)
 
774
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->description));
 
775
                        break;
 
776
                case RHYTHMDB_PROP_SUBTITLE:
 
777
                        if (entry->podcast && entry->podcast->subtitle)
 
778
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->subtitle));
 
779
                        break;
 
780
                case RHYTHMDB_PROP_SUMMARY:
 
781
                        if (entry->podcast && entry->podcast->summary)
 
782
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->summary));
 
783
                        break;
 
784
                case RHYTHMDB_PROP_LANG:
 
785
                        if (entry->podcast && entry->podcast->lang)
 
786
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->lang));
 
787
                        break;
 
788
                case RHYTHMDB_PROP_COPYRIGHT:
 
789
                        if (entry->podcast && entry->podcast->copyright)
 
790
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->copyright));
 
791
                        break;
 
792
                case RHYTHMDB_PROP_IMAGE:
 
793
                        if (entry->podcast && entry->podcast->image)
 
794
                                save_entry_string(ctx, elt_name, rb_refstring_get (entry->podcast->image));
 
795
                        break;
 
796
                case RHYTHMDB_PROP_POST_TIME:
 
797
                        if (entry->podcast)
 
798
                                save_entry_ulong (ctx, elt_name, entry->podcast->post_time, FALSE);
 
799
                        break;                  
 
800
                case RHYTHMDB_PROP_TITLE_SORT_KEY:
 
801
                case RHYTHMDB_PROP_GENRE_SORT_KEY:
 
802
                case RHYTHMDB_PROP_ARTIST_SORT_KEY:
 
803
                case RHYTHMDB_PROP_ALBUM_SORT_KEY:
 
804
                case RHYTHMDB_PROP_TITLE_FOLDED:
 
805
                case RHYTHMDB_PROP_GENRE_FOLDED:
 
806
                case RHYTHMDB_PROP_ARTIST_FOLDED:
 
807
                case RHYTHMDB_PROP_ALBUM_FOLDED:
 
808
                case RHYTHMDB_PROP_LAST_PLAYED_STR:
 
809
                case RHYTHMDB_PROP_HIDDEN:
 
810
                case RHYTHMDB_PROP_PLAYBACK_ERROR:
 
811
                case RHYTHMDB_PROP_FIRST_SEEN_STR:
 
812
                case RHYTHMDB_PROP_SEARCH_MATCH:
 
813
                case RHYTHMDB_NUM_PROPERTIES:
 
814
                        break;
 
815
                }
782
816
        }
783
 
        RHYTHMDB_FWRITE_STATICSTR ("  </entry>\n", ctx->handle);
784
817
 
785
 
#undef RHYTHMDB_FWRITE_ENCODED_STR
786
 
#undef RHYTHMDB_FWRITE_SMALLTYPE
787
 
#undef RHYTHMDB_FWRITE_STATICSTR
788
 
#undef RHYTHMDB_FPUTC
789
 
#undef RHYTHMDB_FWRITE
 
818
        RHYTHMDB_FWRITE_STATICSTR ("  </entry>\n", ctx->handle, ctx->error);
790
819
}
791
820
 
792
821
static void
796
825
        char *name;
797
826
        GString *savepath;
798
827
        FILE *f;
799
 
        struct RhythmDBTreeSaveContext *ctx;
800
 
 
801
 
        ctx = g_new0 (struct RhythmDBTreeSaveContext, 1);
 
828
        struct RhythmDBTreeSaveContext ctx;
802
829
 
803
830
        g_object_get (G_OBJECT (db), "name", &name, NULL);
804
831
 
808
835
        f = fopen (savepath->str, "w");
809
836
 
810
837
        if (!f) {
811
 
                g_warning ("Can't save XML");
 
838
                g_critical ("Can't save XML: %s", g_strerror (errno));
812
839
                goto out;
813
840
        }
814
841
 
815
 
        fprintf (f, "%s\n%s\n", "<?xml version=\"1.0\" standalone=\"yes\"?>",
816
 
                 "<rhythmdb version=\"" RHYTHMDB_TREE_XML_VERSION "\">");
817
 
 
818
 
        ctx->db = db;
819
 
        ctx->handle = f;
 
842
        ctx.db = db;
 
843
        ctx.handle = f;
 
844
        ctx.error = NULL;
 
845
        RHYTHMDB_FWRITE_STATICSTR ("<?xml version=\"1.0\" standalone=\"yes\"?>\n"
 
846
                                   "<rhythmdb version=\"" RHYTHMDB_TREE_XML_VERSION "\">", 
 
847
                                   ctx.handle, ctx.error);
820
848
 
821
849
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_SONG, 
822
850
                                    (RBTreeEntryItFunc)save_entry, 
823
 
                                    NULL, NULL, NULL, ctx);
 
851
                                    NULL, NULL, NULL, &ctx);
824
852
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_IRADIO_STATION, 
825
853
                                    (RBTreeEntryItFunc)save_entry, 
826
 
                                    NULL, NULL, NULL, ctx);
827
 
 
828
 
        fprintf (f, "%s\n", "</rhythmdb>");
829
 
 
830
 
        fclose (f);
831
 
 
832
 
        rename (savepath->str, name);
 
854
                                    NULL, NULL, NULL, &ctx);
 
855
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_PODCAST_POST, 
 
856
                                    (RBTreeEntryItFunc)save_entry, 
 
857
                                    NULL, NULL, NULL, &ctx);
 
858
        rhythmdb_hash_tree_foreach (rdb, RHYTHMDB_ENTRY_TYPE_PODCAST_FEED, 
 
859
                                    (RBTreeEntryItFunc)save_entry, 
 
860
                                    NULL, NULL, NULL, &ctx);
 
861
 
 
862
        RHYTHMDB_FWRITE_STATICSTR ("</rhythmdb>\n", ctx.handle, ctx.error);
 
863
 
 
864
        if (fclose (f) < 0) {
 
865
                g_critical ("Couldn't close %s: %s",
 
866
                            savepath->str,
 
867
                            g_strerror (errno));
 
868
                unlink (savepath->str);
 
869
                goto out;
 
870
        }
 
871
 
 
872
        if (ctx.error != NULL) {
 
873
                g_critical ("Writing to the database failed: %s", ctx.error);
 
874
                g_free (ctx.error);
 
875
                unlink (savepath->str);
 
876
        } else {
 
877
                if (rename (savepath->str, name) < 0) {
 
878
                        g_critical ("Couldn't rename %s to %s: %s",
 
879
                                    name, savepath->str,
 
880
                                    g_strerror (errno));
 
881
                        unlink (savepath->str);
 
882
                }
 
883
        }
 
884
 
833
885
out:
834
886
        g_string_free (savepath, TRUE);
835
887
        g_free (name);
836
 
 
837
 
        g_free (ctx);
 
888
        return;
838
889
}
839
890
 
 
891
#undef RHYTHMDB_FWRITE_ENCODED_STR
 
892
#undef RHYTHMDB_FWRITE_STATICSTR
 
893
#undef RHYTHMDB_FPUTC
 
894
#undef RHYTHMDB_FWRITE
 
895
 
840
896
RhythmDB *
841
897
rhythmdb_tree_new (const char *name)
842
898
{
848
904
}
849
905
 
850
906
static void
851
 
set_entry_album (RhythmDBTree *db, RhythmDBTreeEntry *entry, RhythmDBTreeProperty *artist,
852
 
                 const char *name)
853
 
{
854
 
        entry->album = get_or_create_album (db, RHYTHMDB_TREE_PROPERTY (artist), name);
855
 
        g_hash_table_insert (entry->album->children, RHYTHMDB_TREE_ENTRY (entry), NULL);
856
 
}
857
 
 
858
 
static RhythmDBEntry *
859
 
rhythmdb_tree_entry_allocate (RhythmDBTree *db, RhythmDBEntryType type)
860
 
{
861
 
        RhythmDBTreeEntry *ret;
862
 
        guint i;
863
 
        
864
 
        ret = g_mem_chunk_alloc0 (db->priv->entry_memchunk);
865
 
 
866
 
#ifndef G_DISABLE_ASSERT
867
 
        ret->magic = 0xdeadb33f;
868
 
#endif  
869
 
        /* The refcount is initially 0, we want to set it to 1 */
870
 
        rb_atomic_inc (&ret->refcount);
871
 
 
872
 
        /* Initialize all the properties. */
873
 
        for (i = 0; i < RHYTHMDB_NUM_PROPERTIES; i++) {
874
 
                GType val_type = rhythmdb_get_property_type (RHYTHMDB (db), i);
875
 
                g_value_init (RHYTHMDB_TREE_ENTRY_VALUE (ret, i), val_type);
876
 
 
877
 
                /* Hack to ensure all string values are initialized. */
878
 
                if (val_type == G_TYPE_STRING)
879
 
                        g_value_set_static_string (RHYTHMDB_TREE_ENTRY_VALUE (ret, i), "");
880
 
 
881
 
                if (i == RHYTHMDB_PROP_TRACK_NUMBER)
882
 
                        g_value_set_int (RHYTHMDB_TREE_ENTRY_VALUE (ret, i), -1);
883
 
        }
884
 
        g_value_set_int (RHYTHMDB_TREE_ENTRY_VALUE (ret, RHYTHMDB_PROP_TYPE), type);
885
 
 
886
 
        return ret;
887
 
}
888
 
 
889
 
static gboolean
890
 
rhythmdb_tree_entry_insert (RhythmDBTree *db, RhythmDBTreeEntry *entry,
891
 
                            RhythmDBEntryType type,
892
 
                            const char *uri,
893
 
                            const char *genrename, const char *artistname,
894
 
                            const char *albumname)
895
 
{
 
907
set_entry_album (RhythmDBTree *db, RhythmDBEntry *entry, RhythmDBTreeProperty *artist,
 
908
                 RBRefString *name)
 
909
{
 
910
        struct RhythmDBTreeProperty *prop;
 
911
        prop = get_or_create_album (db, artist, name);
 
912
        g_hash_table_insert (prop->children, entry, NULL);
 
913
        entry->data = prop;
 
914
}
 
915
 
 
916
static void
 
917
rhythmdb_tree_entry_new (RhythmDB *rdb, RhythmDBEntry *entry)
 
918
{
 
919
        RhythmDBTree *db = RHYTHMDB_TREE (rdb);
896
920
        RhythmDBTreeProperty *artist;
897
921
        RhythmDBTreeProperty *genre;
898
 
        char *new_uri;
899
 
 
900
 
        if (g_hash_table_lookup (db->priv->entries, uri))
901
 
                return FALSE;
 
922
 
 
923
        g_assert (entry != NULL);
 
924
 
 
925
        g_return_if_fail (entry->location != NULL);
 
926
 
 
927
        if (entry->title == NULL) {
 
928
                g_warning ("Entry %s has missing title",entry->location);
 
929
                entry->title = rb_refstring_new (_("Unknown"));
 
930
        }
 
931
        if (entry->artist == NULL) {
 
932
                g_warning ("Entry %s has missing artist",entry->location);
 
933
                entry->artist = rb_refstring_new (_("Unknown"));
 
934
        }
 
935
        if (entry->album == NULL) {
 
936
                g_warning ("Entry %s has missing album",entry->location);
 
937
                entry->album = rb_refstring_new (_("Unknown"));
 
938
        }
 
939
        if (entry->genre == NULL) {
 
940
                g_warning ("Entry %s has missing genre",entry->location);
 
941
                entry->genre = rb_refstring_new (_("Unknown"));
 
942
        }
 
943
        if (entry->mimetype == NULL) {
 
944
                g_warning ("Entry %s has missing mimetype",entry->location);
 
945
                entry->mimetype = rb_refstring_new ("unknown/unknown");
 
946
        }
902
947
 
903
948
        /* Initialize the tree structure. */
904
 
        genre = get_or_create_genre (db, type, genrename);
905
 
        artist = get_or_create_artist (db, genre, artistname);
906
 
        set_entry_album (db, entry, artist, albumname);
907
 
 
908
 
        new_uri = g_strdup (uri);
909
 
 
910
 
        g_hash_table_insert (db->priv->entries, new_uri, entry);
911
 
        g_value_set_string_take_ownership (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_LOCATION),
912
 
                                           new_uri);
913
 
        return TRUE;
914
 
}
915
 
 
916
 
 
917
 
static RhythmDBEntry *
918
 
rhythmdb_tree_entry_new (RhythmDB *rdb, RhythmDBEntryType type, const char *uri)
919
 
{
920
 
        RhythmDBTree *db = RHYTHMDB_TREE (rdb);
921
 
        RhythmDBTreeEntry *ret;
922
 
 
923
 
        sanity_check_database (db);
924
 
 
925
 
        ret = rhythmdb_tree_entry_allocate (db, type);
926
 
 
927
 
        if (!rhythmdb_tree_entry_insert (db, ret, type, uri, "", "", "")) {
928
 
                rhythmdb_tree_entry_destroy (db, ret);
929
 
                return NULL;
930
 
        }
931
 
 
932
 
        sanity_check_database (db);
933
 
 
934
 
        return ret;
935
 
}
936
 
 
937
 
void
938
 
rhythmdb_tree_entry_destroy (RhythmDBTree *db, RhythmDBEntry *aentry)
939
 
{
940
 
        RhythmDBTreeEntry *entry = RHYTHMDB_TREE_ENTRY (aentry);
941
 
        rhythmdb_tree_entry_finalize (entry);
942
 
        g_mem_chunk_free (db->priv->entry_memchunk, entry);
 
949
        genre = get_or_create_genre (db, entry->type, entry->genre);
 
950
        artist = get_or_create_artist (db, genre, entry->artist);
 
951
        set_entry_album (db, entry, artist, entry->album);
 
952
 
 
953
        g_hash_table_insert (db->priv->entries, entry->location, entry);
943
954
}
944
955
 
945
956
static RhythmDBTreeProperty *
946
 
rhythmdb_tree_property_new (RhythmDBTree *db, const char *name)
 
957
rhythmdb_tree_property_new (RhythmDBTree *db)
947
958
{
948
959
        RhythmDBTreeProperty *ret = g_mem_chunk_alloc0 (db->priv->property_memchunk);
949
 
        ret->name = g_strdup (name);
950
 
        ret->sort_key = rb_get_sort_key (ret->name);
951
 
        ret->folded = g_utf8_casefold (ret->name, -1);
952
960
#ifndef G_DISABLE_ASSERT
953
961
        ret->magic = 0xf00dbeef;
954
962
#endif  
955
963
        return ret;
956
964
}
957
965
 
958
 
static inline GHashTable *
 
966
static GHashTable *
959
967
get_genres_hash_for_type (RhythmDBTree *db, RhythmDBEntryType type)
960
968
{
961
969
        GHashTable *table;
962
970
 
963
971
        table = g_hash_table_lookup (db->priv->genres, GINT_TO_POINTER (type));
964
972
        if (table == NULL) {
965
 
                table = g_hash_table_new_full (g_str_hash, g_str_equal,
966
 
                                               (GDestroyNotify) g_free,
 
973
                table = g_hash_table_new_full (rb_refstring_hash,
 
974
                                               rb_refstring_equal,
 
975
                                               (GDestroyNotify) rb_refstring_unref,
967
976
                                               NULL);
968
977
                if (table == NULL) {
969
978
                        g_warning ("Out of memory\n");
994
1003
}
995
1004
 
996
1005
static void
997
 
genres_hash_foreach (RhythmDBTree *db, RBHFunc func, gpointer data) {
 
1006
genres_hash_foreach (RhythmDBTree *db, RBHFunc func, gpointer data)
 
1007
{
998
1008
        GenresIterCtxt ctxt;
999
1009
 
1000
1010
        ctxt.db = db;
1003
1013
        g_hash_table_foreach (db->priv->genres, genres_process_one, &ctxt);
1004
1014
}
1005
1015
 
1006
 
static inline RhythmDBTreeProperty *
 
1016
static RhythmDBTreeProperty *
1007
1017
get_or_create_genre (RhythmDBTree *db, RhythmDBEntryType type,
1008
 
                     const char *name)
 
1018
                     RBRefString *name)
1009
1019
{
1010
1020
        RhythmDBTreeProperty *genre;
1011
1021
        GHashTable *table;
1014
1024
        genre = g_hash_table_lookup (table, name);              
1015
1025
 
1016
1026
        if (G_UNLIKELY (genre == NULL)) {
1017
 
                genre = rhythmdb_tree_property_new (db, name);
1018
 
                genre->children = g_hash_table_new_full (g_str_hash, g_str_equal,
1019
 
                                                         (GDestroyNotify) g_free,
 
1027
                genre = rhythmdb_tree_property_new (db);
 
1028
                genre->children = g_hash_table_new_full (rb_refstring_hash, rb_refstring_equal,
 
1029
                                                         (GDestroyNotify) rb_refstring_unref,
1020
1030
                                                         NULL);
1021
 
                g_hash_table_insert (table, g_strdup (name), genre);
 
1031
                rb_refstring_ref (name);
 
1032
                g_hash_table_insert (table, name, genre);
1022
1033
                genre->parent = NULL;
1023
 
                /* rhythmdb_emit_genre_added (RHYTHMDB (db), name); */
1024
1034
        }
1025
1035
 
1026
 
        return RHYTHMDB_TREE_PROPERTY (genre);
 
1036
        return genre;
1027
1037
}
1028
1038
 
1029
1039
static RhythmDBTreeProperty *
1030
1040
get_or_create_artist (RhythmDBTree *db, RhythmDBTreeProperty *genre,
1031
 
                      const char *name)
 
1041
                      RBRefString *name)
1032
1042
{
1033
1043
        RhythmDBTreeProperty *artist;
1034
1044
 
1035
 
        artist = g_hash_table_lookup (RHYTHMDB_TREE_PROPERTY (genre)->children, name);
 
1045
        artist = g_hash_table_lookup (genre->children, name);
1036
1046
 
1037
1047
        if (G_UNLIKELY (artist == NULL)) {
1038
 
                artist = rhythmdb_tree_property_new (db, name);
1039
 
                artist->children = g_hash_table_new_full (g_str_hash, g_str_equal,
1040
 
                                                          (GDestroyNotify) g_free,
 
1048
                artist = rhythmdb_tree_property_new (db);
 
1049
                artist->children = g_hash_table_new_full (rb_refstring_hash, rb_refstring_equal,
 
1050
                                                          (GDestroyNotify) rb_refstring_unref,
1041
1051
                                                          NULL);
1042
 
                g_hash_table_insert (genre->children, g_strdup (name), artist);
 
1052
                rb_refstring_ref (name);
 
1053
                g_hash_table_insert (genre->children, name, artist);
1043
1054
                artist->parent = genre;
1044
 
                /* rhythmdb_emit_artist_added (RHYTHMDB (db), name); */
1045
1055
        }
1046
1056
 
1047
 
        return RHYTHMDB_TREE_PROPERTY (artist);
 
1057
        return artist;
1048
1058
}
1049
1059
 
1050
1060
static RhythmDBTreeProperty *
1051
1061
get_or_create_album (RhythmDBTree *db, RhythmDBTreeProperty *artist,
1052
 
                     const char *name)
 
1062
                     RBRefString *name)
1053
1063
{
1054
1064
        RhythmDBTreeProperty *album;
1055
1065
 
1056
 
        album = g_hash_table_lookup (RHYTHMDB_TREE_PROPERTY (artist)->children, name);
 
1066
        album = g_hash_table_lookup (artist->children, name);
1057
1067
 
1058
1068
        if (G_UNLIKELY (album == NULL)) {
1059
 
                album = rhythmdb_tree_property_new (db, name);
 
1069
                album = rhythmdb_tree_property_new (db);
1060
1070
                album->children = g_hash_table_new (g_direct_hash, g_direct_equal);
1061
 
                g_hash_table_insert (artist->children, g_strdup (name), album);
 
1071
                rb_refstring_ref (name);
 
1072
                g_hash_table_insert (artist->children, name, album);
1062
1073
                album->parent = artist;
1063
 
                /* rhythmdb_emit_album_added (RHYTHMDB (db), name); */
1064
1074
        }
1065
1075
 
1066
 
        return RHYTHMDB_TREE_PROPERTY (album);
1067
 
}
1068
 
 
1069
 
static inline void
1070
 
handle_genre_deletion (RhythmDBTree *db, const char *name)
1071
 
{
1072
 
/*      if (!db->priv->finalizing) */
1073
 
/*              rhythmdb_emit_genre_deleted (RHYTHMDB (db), name); */
1074
 
}
1075
 
 
1076
 
static inline void
1077
 
handle_artist_deletion (RhythmDBTree *db, const char *name)
1078
 
{
1079
 
/*      if (!db->priv->finalizing) */
1080
 
/*              rhythmdb_emit_artist_deleted (RHYTHMDB (db), name); */
1081
 
}
1082
 
 
1083
 
static inline void
1084
 
handle_album_deletion (RhythmDBTree *db, const char *name)
1085
 
{
1086
 
/*      if (!db->priv->finalizing) */
1087
 
/*              rhythmdb_emit_album_deleted (RHYTHMDB (db), name); */
 
1076
        return album;
1088
1077
}
1089
1078
 
1090
1079
static gboolean
1091
 
remove_child (RhythmDBTreeProperty *parent, gpointer entry)
 
1080
remove_child (RhythmDBTreeProperty *parent, gconstpointer data)
1092
1081
{
1093
 
        RHYTHMDB_TREE_PROPERTY (parent);
1094
 
        g_assert (g_hash_table_remove (parent->children, entry));
 
1082
        g_assert (g_hash_table_remove (parent->children, data));
1095
1083
        if (g_hash_table_size (parent->children) <= 0) {
1096
1084
                return TRUE;
1097
1085
        }
1099
1087
}
1100
1088
 
1101
1089
static void
1102
 
remove_entry_from_album (RhythmDBTree *db, RhythmDBTreeEntry *entry)
 
1090
remove_entry_from_album (RhythmDBTree *db, RhythmDBEntry *entry)
1103
1091
{
1104
 
        char *cur_albumname;
1105
 
        char *cur_artistname;
1106
 
        char *cur_genrename;
1107
1092
        GHashTable *table;
1108
1093
                        
1109
 
        cur_albumname = g_strdup (get_entry_album_name (entry));
1110
 
        cur_artistname = g_strdup (get_entry_artist_name (entry));
1111
 
        cur_genrename = g_strdup (get_entry_genre_name (entry));
1112
 
 
1113
 
        table = get_genres_hash_for_type (db, RHYTHMDB_TREE_ENTRY_GET_TYPE (entry));
1114
 
        if (remove_child (RHYTHMDB_TREE_PROPERTY (entry->album), entry)) {
1115
 
 
1116
 
                handle_album_deletion (db, cur_albumname);
1117
 
 
1118
 
                if (remove_child (RHYTHMDB_TREE_PROPERTY (entry->album->parent), cur_albumname)) {
1119
 
                        handle_artist_deletion (db, cur_artistname);
1120
 
 
1121
 
                        if (remove_child (RHYTHMDB_TREE_PROPERTY (entry->album->parent->parent),
1122
 
                                          cur_artistname)) {
1123
 
                                handle_genre_deletion (db, cur_albumname);
1124
 
                                destroy_tree_property (entry->album->parent->parent);
1125
 
                                g_hash_table_remove (table, cur_genrename);
 
1094
        rb_refstring_ref (entry->genre);
 
1095
        rb_refstring_ref (entry->artist);
 
1096
        rb_refstring_ref (entry->album);
 
1097
 
 
1098
        table = get_genres_hash_for_type (db, entry->type);
 
1099
        if (remove_child (RHYTHMDB_TREE_PROPERTY_FROM_ENTRY (entry), entry)) {
 
1100
                if (remove_child (RHYTHMDB_TREE_PROPERTY_FROM_ENTRY (entry)->parent,
 
1101
                                  entry->album)) {
 
1102
 
 
1103
                        if (remove_child (RHYTHMDB_TREE_PROPERTY_FROM_ENTRY (entry)->parent->parent,
 
1104
                                          entry->artist)) {
 
1105
                                destroy_tree_property (RHYTHMDB_TREE_PROPERTY_FROM_ENTRY (entry)->parent->parent);
 
1106
                                g_assert (g_hash_table_remove (table, entry->genre));
1126
1107
                        }
1127
 
                        destroy_tree_property (entry->album->parent);
 
1108
                        destroy_tree_property (RHYTHMDB_TREE_PROPERTY_FROM_ENTRY (entry)->parent);
1128
1109
                }
1129
1110
 
1130
 
                destroy_tree_property (entry->album);
 
1111
                destroy_tree_property (RHYTHMDB_TREE_PROPERTY_FROM_ENTRY (entry));
1131
1112
        }
1132
1113
 
1133
 
        g_free (cur_genrename);
1134
 
        g_free (cur_artistname);
1135
 
        g_free (cur_albumname);
 
1114
        rb_refstring_unref (entry->genre);
 
1115
        rb_refstring_unref (entry->artist);
 
1116
        rb_refstring_unref (entry->album);
1136
1117
}
1137
1118
 
1138
 
static void
1139
 
rhythmdb_tree_entry_set (RhythmDB *adb, RhythmDBEntry *aentry,
 
1119
static gboolean
 
1120
rhythmdb_tree_entry_set (RhythmDB *adb, RhythmDBEntry *entry,
1140
1121
                         guint propid, const GValue *value)
1141
1122
{
1142
1123
        RhythmDBTree *db = RHYTHMDB_TREE (adb);
1143
 
        RhythmDBTreeEntry *entry = RHYTHMDB_TREE_ENTRY (aentry);
1144
1124
        RhythmDBEntryType type;
1145
1125
 
1146
 
        type = RHYTHMDB_TREE_ENTRY_GET_TYPE (entry);
1147
 
 
1148
 
        sanity_check_database (db);
1149
 
 
1150
 
        if (entry->deleted)
1151
 
                return;
 
1126
        type = entry->type;
1152
1127
 
1153
1128
        /* Handle special properties */
1154
1129
        switch (propid)
1155
1130
        {
 
1131
        case RHYTHMDB_PROP_LOCATION:
 
1132
        {
 
1133
                /* We have to use the string in the entry itself as the hash key,
 
1134
                 * otherwise either we leak it, or the string vanishes when the
 
1135
                 * GValue is freed; this means we have to do the entry modification
 
1136
                 * here, rather than letting rhythmdb_entry_set_internal do it.
 
1137
                 */
 
1138
                g_assert (g_hash_table_lookup (db->priv->entries, entry->location) != NULL);
 
1139
 
 
1140
                g_hash_table_remove (db->priv->entries, entry->location);
 
1141
 
 
1142
                g_free (entry->location);
 
1143
                entry->location = g_strdup (g_value_get_string (value));
 
1144
                g_hash_table_insert (db->priv->entries, entry->location, entry);
 
1145
 
 
1146
                return TRUE;
 
1147
        }
1156
1148
        case RHYTHMDB_PROP_ALBUM:
1157
1149
        {
1158
1150
                const char *albumname = g_value_get_string (value);
1159
1151
 
1160
 
                if (strcmp (get_entry_album_name (entry), albumname)) {
1161
 
                        char *cur_genrename, *cur_artistname;
 
1152
                if (strcmp (rb_refstring_get (entry->album), albumname)) {
1162
1153
                        RhythmDBTreeProperty *artist;
1163
1154
                        RhythmDBTreeProperty *genre;                    
1164
1155
 
1165
 
                        cur_artistname = g_strdup (get_entry_artist_name (entry));
1166
 
                        cur_genrename = g_strdup (get_entry_genre_name (entry));
 
1156
                        rb_refstring_ref (entry->genre);
 
1157
                        rb_refstring_ref (entry->artist);
 
1158
                        rb_refstring_ref (entry->album);
1167
1159
 
1168
1160
                        remove_entry_from_album (db, entry); 
1169
1161
 
1170
 
                        genre = RHYTHMDB_TREE_PROPERTY (get_or_create_genre (db, type, cur_genrename)); 
1171
 
                        artist = RHYTHMDB_TREE_PROPERTY (get_or_create_artist (db, genre, cur_artistname)); 
1172
 
                        set_entry_album (db, entry, artist, albumname);
1173
 
 
1174
 
                        sanity_check_entry_tree (entry);
1175
 
 
1176
 
                        g_free (cur_artistname);
1177
 
                        g_free (cur_genrename);
 
1162
                        genre = get_or_create_genre (db, type, entry->genre); 
 
1163
                        artist = get_or_create_artist (db, genre, entry->artist); 
 
1164
                        set_entry_album (db, entry, artist, rb_refstring_new (albumname));
 
1165
 
 
1166
                        rb_refstring_unref (entry->genre);
 
1167
                        rb_refstring_unref (entry->artist);
 
1168
                        rb_refstring_unref (entry->album);
1178
1169
                }
 
1170
                break;
1179
1171
        }
1180
 
        break;
1181
1172
        case RHYTHMDB_PROP_ARTIST:
1182
1173
        {
1183
1174
                const char *artistname = g_value_get_string (value);
1184
 
                char *cur_artistname = get_entry_artist_name (entry);
1185
1175
 
1186
 
                if (strcmp (cur_artistname, artistname)) {
 
1176
                if (strcmp (rb_refstring_get (entry->artist), artistname)) {
1187
1177
                        RhythmDBTreeProperty *new_artist;
1188
1178
                        RhythmDBTreeProperty *genre;                    
1189
 
                        char *cur_genrename, *cur_albumname;
1190
1179
 
1191
 
                        cur_artistname = g_strdup (cur_artistname);
1192
 
                        cur_albumname = g_strdup (get_entry_album_name (entry));
1193
 
                        cur_genrename = g_strdup (get_entry_genre_name (entry));
 
1180
                        rb_refstring_ref (entry->genre);
 
1181
                        rb_refstring_ref (entry->artist);
 
1182
                        rb_refstring_ref (entry->album);
1194
1183
 
1195
1184
                        remove_entry_from_album (db, entry); 
1196
1185
 
1197
 
                        genre = RHYTHMDB_TREE_PROPERTY (get_or_create_genre (db, type, cur_genrename)); 
1198
 
                        new_artist = RHYTHMDB_TREE_PROPERTY (get_or_create_artist (db, genre, artistname)); 
1199
 
 
1200
 
                        set_entry_album (db, entry, new_artist, cur_albumname);
1201
 
 
1202
 
                        sanity_check_entry_tree (entry);
1203
 
 
1204
 
                        g_free (cur_genrename);
1205
 
                        g_free (cur_albumname);
1206
 
                        g_free (cur_artistname);
 
1186
                        genre = get_or_create_genre (db, type, entry->genre); 
 
1187
                        new_artist = get_or_create_artist (db, genre,
 
1188
                                                           rb_refstring_new (artistname)); 
 
1189
                        set_entry_album (db, entry, new_artist, entry->album);
 
1190
 
 
1191
                        rb_refstring_unref (entry->genre);
 
1192
                        rb_refstring_unref (entry->artist);
 
1193
                        rb_refstring_unref (entry->album);
1207
1194
                }
 
1195
                break;
1208
1196
        }
1209
 
        break;
1210
1197
        case RHYTHMDB_PROP_GENRE:
1211
1198
        {
1212
1199
                const char *genrename = g_value_get_string (value);
1213
 
                char *cur_genrename = get_entry_genre_name (entry);
1214
1200
 
1215
 
                if (strcmp (cur_genrename, genrename)) {
 
1201
                if (strcmp (rb_refstring_get (entry->genre), genrename)) {
1216
1202
                        RhythmDBTreeProperty *new_genre;                        
1217
1203
                        RhythmDBTreeProperty *new_artist;
1218
 
                        char *artistname;
1219
 
                        char *albumname;
1220
 
 
1221
 
                        cur_genrename = g_strdup (cur_genrename);
1222
 
 
1223
 
                        artistname = g_strdup (get_entry_artist_name (entry));
1224
 
                        albumname = g_strdup (get_entry_album_name (entry));
1225
 
                        
 
1204
 
 
1205
                        rb_refstring_ref (entry->genre);
 
1206
                        rb_refstring_ref (entry->artist);
 
1207
                        rb_refstring_ref (entry->album);
 
1208
 
1226
1209
                        remove_entry_from_album (db, entry); 
1227
1210
 
1228
 
                        new_genre = RHYTHMDB_TREE_PROPERTY (get_or_create_genre (db, type, genrename)); 
1229
 
                        new_artist = RHYTHMDB_TREE_PROPERTY (get_or_create_artist (db, new_genre, artistname)); 
1230
 
 
1231
 
                        set_entry_album (db, entry, new_artist, albumname);
1232
 
 
1233
 
                        sanity_check_entry_tree (entry);
1234
 
 
1235
 
                        g_free (cur_genrename);
1236
 
                        g_free (artistname);
1237
 
                        g_free (albumname);
 
1211
                        new_genre = get_or_create_genre (db, type,
 
1212
                                                         rb_refstring_new (genrename));
 
1213
                        new_artist = get_or_create_artist (db, new_genre, entry->artist);
 
1214
                        set_entry_album (db, entry, new_artist, entry->album);
 
1215
 
 
1216
                        rb_refstring_unref (entry->genre);
 
1217
                        rb_refstring_unref (entry->artist);
 
1218
                        rb_refstring_unref (entry->album);
1238
1219
                }
1239
 
        }
1240
 
        break;
1241
 
        default:
1242
 
                g_value_reset (RHYTHMDB_TREE_ENTRY_VALUE (entry, propid));
1243
 
                g_value_copy (value, RHYTHMDB_TREE_ENTRY_VALUE (entry, propid));
1244
 
                break;
1245
 
        }
1246
 
 
1247
 
        sanity_check_database (db);
1248
 
}
1249
 
 
1250
 
static void
1251
 
rhythmdb_tree_entry_get (RhythmDB *adb, RhythmDBEntry *aentry,
1252
 
                         guint propid, GValue *value)
1253
 
{
1254
 
        RhythmDBTree *db = RHYTHMDB_TREE (adb);
1255
 
        RhythmDBTreeEntry *entry = RHYTHMDB_TREE_ENTRY (aentry);
1256
 
 
1257
 
        sanity_check_database (db);
1258
 
 
1259
 
        if (G_UNLIKELY (entry->deleted)) {
1260
 
                g_value_copy (RHYTHMDB_TREE_ENTRY_VALUE (entry, propid), value);
1261
 
                return;
1262
 
        }
1263
 
 
1264
 
        switch (propid)
1265
 
        {
1266
 
        /* Handle special properties */
1267
 
        case RHYTHMDB_PROP_ALBUM:
1268
 
                g_value_set_static_string (value, get_entry_album_name (entry));
1269
 
                break;
1270
 
        case RHYTHMDB_PROP_ARTIST:
1271
 
                g_value_set_static_string (value, get_entry_artist_name (entry));
1272
 
                break;
1273
 
        case RHYTHMDB_PROP_GENRE:
1274
 
                g_value_set_static_string (value, get_entry_genre_name (entry));
1275
 
                break;
1276
 
        case RHYTHMDB_PROP_ALBUM_SORT_KEY:
1277
 
                g_value_set_static_string (value, get_entry_album_sort_key (entry));
1278
 
                break;
1279
 
        case RHYTHMDB_PROP_ARTIST_SORT_KEY:
1280
 
                g_value_set_static_string (value, get_entry_artist_sort_key (entry));
1281
 
                break;
1282
 
        case RHYTHMDB_PROP_GENRE_SORT_KEY:
1283
 
                g_value_set_static_string (value, get_entry_genre_sort_key (entry));
1284
 
                break;
1285
 
        case RHYTHMDB_PROP_ALBUM_FOLDED:
1286
 
                g_value_set_static_string (value, get_entry_album_folded (entry));
1287
 
                break;
1288
 
        case RHYTHMDB_PROP_ARTIST_FOLDED:
1289
 
                g_value_set_static_string (value, get_entry_artist_folded (entry));
1290
 
                break;
1291
 
        case RHYTHMDB_PROP_GENRE_FOLDED:
1292
 
                g_value_set_static_string (value, get_entry_genre_folded (entry));
1293
 
                break;
1294
 
        /* Handle other string properties */
1295
 
        case RHYTHMDB_PROP_TITLE:
1296
 
                g_value_set_static_string (value, g_value_get_string (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_TITLE)));
1297
 
                break;
1298
 
        case RHYTHMDB_PROP_TITLE_SORT_KEY:
1299
 
                g_value_set_static_string (value, g_value_get_string (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_TITLE_SORT_KEY)));
1300
 
                break;
1301
 
        case RHYTHMDB_PROP_LOCATION:
1302
 
                g_value_set_static_string (value, g_value_get_string (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_LOCATION)));
1303
 
                break;
1304
 
        case RHYTHMDB_PROP_LAST_PLAYED_STR:
1305
 
                g_value_set_static_string (value, g_value_get_string (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_LAST_PLAYED_STR)));
1306
 
                break;
1307
 
        case RHYTHMDB_PROP_TRACK_GAIN:
1308
 
                g_value_set_double (value, g_value_get_double (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_TRACK_GAIN)));
1309
 
                break;
1310
 
        case RHYTHMDB_PROP_TRACK_PEAK:
1311
 
                g_value_set_double (value, g_value_get_double (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_TRACK_PEAK)));
1312
 
                break;
1313
 
        case RHYTHMDB_PROP_ALBUM_GAIN:
1314
 
                g_value_set_double (value, g_value_get_double (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_ALBUM_GAIN)));
1315
 
                break;
1316
 
        case RHYTHMDB_PROP_ALBUM_PEAK:
1317
 
                g_value_set_double (value, g_value_get_double (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_ALBUM_PEAK)));
1318
 
                break;
1319
 
        default:
1320
 
                g_value_copy (RHYTHMDB_TREE_ENTRY_VALUE (entry, propid), value);
1321
 
                break;
1322
 
        }
1323
 
 
1324
 
        sanity_check_database (RHYTHMDB_TREE (db));
1325
 
}
1326
 
 
1327
 
static void
1328
 
rhythmdb_tree_entry_finalize (RhythmDBTreeEntry *entry)
1329
 
{
1330
 
        guint i;
1331
 
        for (i = 0; i < RHYTHMDB_NUM_PROPERTIES; i++) {
1332
 
                g_value_unset (RHYTHMDB_TREE_ENTRY_VALUE (entry, i));
1333
 
        }
1334
 
#ifndef G_DISABLE_ASSERT
1335
 
        entry->magic = 0xf33df33d;
1336
 
#endif
1337
 
}
1338
 
 
1339
 
static void 
1340
 
rhythmdb_tree_entry_delete_real (RhythmDB *adb, RhythmDBEntry *aentry)
1341
 
{
1342
 
        RhythmDBTree *db = RHYTHMDB_TREE (adb);
1343
 
        RhythmDBTreeEntry *entry = RHYTHMDB_TREE_ENTRY (aentry);
1344
 
 
1345
 
        if (entry->deleted)
1346
 
                return;
1347
 
 
1348
 
        entry->deleted = TRUE;
1349
 
 
1350
 
        
1351
 
        /* We store these properties back in the entry temporarily so that later
1352
 
           callbacks can retreive the value even though the entry is removed from
1353
 
           the indexed tree.
1354
 
        */
1355
 
        g_value_set_string (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_GENRE),
1356
 
                            get_entry_genre_name (entry));
1357
 
        g_value_set_string (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_ARTIST),
1358
 
                            get_entry_artist_name (entry));
1359
 
        g_value_set_string (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_ALBUM),
1360
 
                            get_entry_album_name (entry));
 
1220
                break;
 
1221
        }
 
1222
        default:
 
1223
                break;
 
1224
        }
 
1225
 
 
1226
        return FALSE;
 
1227
}
 
1228
 
 
1229
static void
 
1230
rhythmdb_tree_entry_delete (RhythmDB *adb, RhythmDBEntry *entry)
 
1231
{
 
1232
        RhythmDBTree *db = RHYTHMDB_TREE (adb);
 
1233
 
1361
1234
        remove_entry_from_album (db, entry); 
1362
 
}
1363
 
 
1364
 
static void
1365
 
rhythmdb_tree_entry_delete (RhythmDB *adb, RhythmDBEntry *aentry)
1366
 
{
1367
 
        RhythmDBTree *db = RHYTHMDB_TREE (adb);
1368
 
        RhythmDBTreeEntry *entry = RHYTHMDB_TREE_ENTRY (aentry);
1369
 
        const char *uri;
1370
 
 
1371
 
        sanity_check_database (db);
1372
 
        
1373
 
        rhythmdb_tree_entry_delete_real (adb, aentry);
1374
 
 
1375
 
        uri = g_value_get_string (RHYTHMDB_TREE_ENTRY_VALUE (entry, RHYTHMDB_PROP_LOCATION));
1376
 
        g_assert (g_hash_table_lookup (db->priv->entries, uri) != NULL);
1377
 
 
1378
 
        g_hash_table_remove (db->priv->entries, uri);
1379
 
 
1380
 
        sanity_check_database (db);
 
1235
 
 
1236
        g_assert (g_hash_table_lookup (db->priv->entries, entry->location) != NULL);
 
1237
 
 
1238
        g_hash_table_remove (db->priv->entries, entry->location);
1381
1239
}
1382
1240
 
1383
1241
typedef struct {
1386
1244
} RbEntryRemovalCtxt;
1387
1245
 
1388
1246
static gboolean
1389
 
remove_one_song (gchar *uri, RhythmDBTreeEntry *entry, 
 
1247
remove_one_song (gchar *uri, RhythmDBEntry *entry, 
1390
1248
                 RbEntryRemovalCtxt *ctxt)
1391
1249
{
1392
1250
        g_return_val_if_fail (entry != NULL, FALSE);
1393
1251
 
1394
 
        if (RHYTHMDB_TREE_ENTRY_GET_TYPE (RHYTHMDB_TREE_ENTRY (entry)) == ctxt->type) {
 
1252
        if (entry->type == ctxt->type) {
1395
1253
                rhythmdb_emit_entry_deleted (ctxt->db, entry);
1396
 
                rhythmdb_tree_entry_delete_real (ctxt->db, entry);
 
1254
                remove_entry_from_album (RHYTHMDB_TREE (ctxt->db), entry); 
1397
1255
                return TRUE;
1398
1256
        }
1399
1257
        return FALSE;
1419
1277
#ifndef G_DISABLE_ASSERT
1420
1278
        prop->magic = 0xf33df33d;
1421
1279
#endif
1422
 
        g_free (prop->name);
1423
 
        g_free (prop->sort_key);
1424
 
        g_free (prop->folded);
1425
1280
        g_hash_table_destroy (prop->children);
1426
1281
}
1427
1282
 
1428
 
typedef void (*RhythmDBTreeTraversalFunc) (RhythmDBTree *db, RhythmDBTreeEntry *entry, gpointer data);
 
1283
typedef void (*RhythmDBTreeTraversalFunc) (RhythmDBTree *db, RhythmDBEntry *entry, gpointer data);
1429
1284
typedef void (*RhythmDBTreeAlbumTraversalFunc) (RhythmDBTree *db, RhythmDBTreeProperty *album, gpointer data);
1430
1285
 
1431
1286
struct RhythmDBTreeTraversalData
1437
1292
        gboolean *cancel;
1438
1293
};
1439
1294
 
1440
 
gboolean
 
1295
static gboolean
1441
1296
rhythmdb_tree_evaluate_query (RhythmDB *adb, GPtrArray *query,
1442
 
                              RhythmDBEntry *aentry)
 
1297
                              RhythmDBEntry *entry)
1443
1298
{
1444
1299
        RhythmDBTree *db = RHYTHMDB_TREE (adb);
1445
 
        RhythmDBTreeEntry *entry = RHYTHMDB_TREE_ENTRY (aentry);
1446
1300
        guint i;
1447
1301
        guint last_disjunction;
1448
1302
 
1461
1315
        return FALSE;
1462
1316
}
1463
1317
 
1464
 
static gboolean
1465
 
evaluate_conjunctive_subquery (RhythmDBTree *db, GPtrArray *query,
1466
 
                               guint base, guint max, RhythmDBTreeEntry *entry)
1467
 
 
1468
 
{
 
1318
#define RHYTHMDB_PROPERTY_COMPARE(OP) \
 
1319
                        switch (rhythmdb_get_property_type (db, data->propid)) { \
 
1320
                        case G_TYPE_STRING: \
 
1321
                                if (strcmp (rhythmdb_entry_get_string (entry, data->propid), \
 
1322
                                            g_value_get_string (data->val)) OP 0) \
 
1323
                                        return FALSE; \
 
1324
                                break; \
 
1325
                        case G_TYPE_ULONG: \
 
1326
                                if (rhythmdb_entry_get_ulong (entry, data->propid) OP \
 
1327
                                    g_value_get_ulong (data->val)) \
 
1328
                                        return FALSE; \
 
1329
                                break; \
 
1330
                        case G_TYPE_BOOLEAN: \
 
1331
                                if (rhythmdb_entry_get_boolean (entry, data->propid) OP \
 
1332
                                    g_value_get_boolean (data->val)) \
 
1333
                                        return FALSE; \
 
1334
                                break; \
 
1335
                        case G_TYPE_UINT64: \
 
1336
                                if (rhythmdb_entry_get_uint64 (entry, data->propid) OP \
 
1337
                                    g_value_get_uint64 (data->val)) \
 
1338
                                        return FALSE; \
 
1339
                                break; \
 
1340
                        case G_TYPE_DOUBLE: \
 
1341
                                if (rhythmdb_entry_get_double (entry, data->propid) OP \
 
1342
                                    g_value_get_double (data->val)) \
 
1343
                                        return FALSE; \
 
1344
                                break; \
 
1345
                        default: \
 
1346
                                g_assert_not_reached (); \
 
1347
                        }
 
1348
 
 
1349
static gboolean
 
1350
search_match_properties (RhythmDB *db, RhythmDBEntry *entry, gchar **words)
 
1351
{
 
1352
        const RhythmDBPropType props[] = {
 
1353
                RHYTHMDB_PROP_TITLE_FOLDED,
 
1354
                RHYTHMDB_PROP_ALBUM_FOLDED,
 
1355
                RHYTHMDB_PROP_ARTIST_FOLDED,
 
1356
                RHYTHMDB_PROP_GENRE_FOLDED
 
1357
        };
 
1358
        gboolean islike = TRUE;
 
1359
        gchar **current;
 
1360
        int i;
 
1361
        
 
1362
        for (current = words; *current != NULL; current++) {
 
1363
                gboolean word_found = FALSE;
 
1364
 
 
1365
                for (i = 0; i < G_N_ELEMENTS (props); i++) {
 
1366
                        const char *entry_string = rhythmdb_entry_get_string (entry, props[i]);
 
1367
                        if (entry_string && (strstr (entry_string, *current) != NULL)) {
 
1368
                                /* the word was found, go to the next one */    
 
1369
                                word_found = TRUE;
 
1370
                                break;
 
1371
                        }
 
1372
                }
 
1373
                if (!word_found) {
 
1374
                        /* the word wasn't in any of the properties*/
 
1375
                        islike = FALSE;
 
1376
                        break;
 
1377
                }
 
1378
        }
 
1379
 
 
1380
        return islike;
 
1381
}
 
1382
 
 
1383
static gboolean
 
1384
evaluate_conjunctive_subquery (RhythmDBTree *dbtree, GPtrArray *query,
 
1385
                               guint base, guint max, RhythmDBEntry *entry)
 
1386
 
 
1387
{
 
1388
        RhythmDB *db = (RhythmDB *) dbtree;
1469
1389
        guint i;
1470
 
        entry = RHYTHMDB_TREE_ENTRY (entry);
1471
1390
/* Optimization possibility - we may get here without actually having
1472
1391
 * anything in the query.  It would be faster to instead just merge
1473
1392
 * the child hash table into the query result hash.
1479
1398
                case RHYTHMDB_QUERY_SUBQUERY:
1480
1399
                {
1481
1400
                        gboolean matched = FALSE;
1482
 
                        GList *conjunctions = split_query_by_disjunctions (db, data->subquery);
 
1401
                        GList *conjunctions = split_query_by_disjunctions (dbtree, data->subquery);
1483
1402
                        GList *tem;
1484
1403
 
 
1404
                        if (conjunctions == NULL)
 
1405
                                matched = TRUE;
 
1406
 
1485
1407
                        for (tem = conjunctions; tem; tem = tem->next) {
1486
1408
                                GPtrArray *subquery = tem->data;
1487
 
                                if (!matched && evaluate_conjunctive_subquery (db, subquery,
 
1409
                                if (!matched && evaluate_conjunctive_subquery (dbtree, subquery,
1488
1410
                                                                               0, subquery->len,
1489
1411
                                                                               entry)) {
1490
1412
                                        matched = TRUE;
1496
1418
                                return FALSE;
1497
1419
                }
1498
1420
                break;
 
1421
                case RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN:
 
1422
                case RHYTHMDB_QUERY_PROP_CURRENT_TIME_NOT_WITHIN:
 
1423
                {
 
1424
                        gulong relative_time;
 
1425
                        GTimeVal current_time;
 
1426
 
 
1427
                        g_assert (rhythmdb_get_property_type (db, data->propid) == G_TYPE_ULONG);
 
1428
 
 
1429
                        relative_time = g_value_get_ulong (data->val);
 
1430
                        g_get_current_time  (&current_time);
 
1431
 
 
1432
                        if (data->type == RHYTHMDB_QUERY_PROP_CURRENT_TIME_WITHIN)
 
1433
                                return (rhythmdb_entry_get_ulong (entry, data->propid) >= (current_time.tv_sec - relative_time));
 
1434
                        else
 
1435
                                return (rhythmdb_entry_get_ulong (entry, data->propid) < (current_time.tv_sec - relative_time));
 
1436
 
 
1437
                        break;
 
1438
                }
1499
1439
                case RHYTHMDB_QUERY_PROP_LIKE:
1500
1440
                case RHYTHMDB_QUERY_PROP_NOT_LIKE:
1501
 
                        if (G_VALUE_TYPE (data->val) == G_TYPE_STRING) {
 
1441
                {
 
1442
                        if (rhythmdb_get_property_type (db, data->propid) == G_TYPE_STRING) {
1502
1443
                                gboolean islike;
1503
 
                                const char *stra, *strb;
1504
 
 
1505
 
                                switch (data->propid)
1506
 
                                {
1507
 
                                case RHYTHMDB_PROP_ALBUM_FOLDED:
1508
 
                                        stra = get_entry_album_folded (entry);
1509
 
                                        break;
1510
 
                                case RHYTHMDB_PROP_ARTIST_FOLDED:
1511
 
                                        stra = get_entry_artist_folded (entry);
1512
 
                                        break;
1513
 
                                case RHYTHMDB_PROP_GENRE_FOLDED:
1514
 
                                        stra = get_entry_genre_folded (entry);
1515
 
                                        break;
1516
 
                                default:
1517
 
                                        stra = g_value_get_string (RHYTHMDB_TREE_ENTRY_VALUE (entry, data->propid));
 
1444
 
 
1445
                                if (data->propid == RHYTHMDB_PROP_SEARCH_MATCH) {
 
1446
                                        /* this is a special property, that should match several things */
 
1447
                                        islike = search_match_properties (db, entry, g_value_get_boxed (data->val));
 
1448
                                        
 
1449
                                } else {
 
1450
                                        const gchar *value_string = g_value_get_string (data->val);
 
1451
                                        const char *entry_string = rhythmdb_entry_get_string (entry, data->propid);
 
1452
 
 
1453
                                        /* check in case the property is NULL, the value should never be NULL */
 
1454
                                        if (entry_string == NULL)
 
1455
                                                return FALSE;
 
1456
 
 
1457
                                        islike = (strstr (entry_string, value_string) != NULL);
1518
1458
                                }
1519
1459
 
1520
 
                                strb = g_value_get_string (data->val);
1521
 
                                islike = (strstr (stra, strb) != NULL);
1522
 
                                if (data->type == RHYTHMDB_QUERY_PROP_LIKE
1523
 
                                    && !islike)
1524
 
                                        return FALSE;
1525
 
                                else if (data->type == RHYTHMDB_QUERY_PROP_NOT_LIKE
1526
 
                                    && islike)
1527
 
                                        return FALSE;
1528
 
                        } else {
1529
 
                                if (rb_gvalue_compare (RHYTHMDB_TREE_ENTRY_VALUE (entry, data->propid),
1530
 
                                                       data->val) != 0)
1531
 
                                        return FALSE;
1532
 
                        }
1533
 
                        break;
 
1460
                                if ((data->type == RHYTHMDB_QUERY_PROP_LIKE) ^ islike)
 
1461
                                        return FALSE;
 
1462
                                else
 
1463
                                        continue;
 
1464
                                break;
 
1465
                        } 
 
1466
                        /* Fall through */
 
1467
                }
1534
1468
                case RHYTHMDB_QUERY_PROP_EQUALS:
1535
 
                        switch (data->propid)
1536
 
                        {
1537
 
                        case RHYTHMDB_PROP_ALBUM:
1538
 
                                if (strcmp (get_entry_album_name (entry),
1539
 
                                            g_value_get_string (data->val)))
1540
 
                                        return FALSE;
1541
 
                                break;
1542
 
                        case RHYTHMDB_PROP_ARTIST:
1543
 
                                if (strcmp (get_entry_artist_name (entry),
1544
 
                                            g_value_get_string (data->val)))
1545
 
                                        return FALSE;
1546
 
                                break;
1547
 
                        case RHYTHMDB_PROP_GENRE:
1548
 
                                if (strcmp (get_entry_genre_name (entry),
1549
 
                                            g_value_get_string (data->val)))
1550
 
                                        return FALSE;
1551
 
                                break;                          
1552
 
                        default:
1553
 
                                if (rb_gvalue_compare (RHYTHMDB_TREE_ENTRY_VALUE (entry, data->propid),
1554
 
                                                       data->val) != 0)
1555
 
                                        return FALSE;
1556
 
                                
1557
 
                        }
 
1469
                {
 
1470
                        RHYTHMDB_PROPERTY_COMPARE (!=)
1558
1471
                        break;
 
1472
                }
1559
1473
                case RHYTHMDB_QUERY_PROP_GREATER:
1560
 
                        if (rb_gvalue_compare (RHYTHMDB_TREE_ENTRY_VALUE (entry, data->propid),
1561
 
                                               data->val) <= 0)
1562
 
                                return FALSE;
 
1474
                        RHYTHMDB_PROPERTY_COMPARE (<)
1563
1475
                        break;
1564
1476
                case RHYTHMDB_QUERY_PROP_LESS:
1565
 
                        if (rb_gvalue_compare (RHYTHMDB_TREE_ENTRY_VALUE (entry, data->propid),
1566
 
                                               data->val) >= 0)
1567
 
                                return FALSE;
 
1477
                        RHYTHMDB_PROPERTY_COMPARE (>)
1568
1478
                        break;
1569
1479
                case RHYTHMDB_QUERY_END:
1570
1480
                case RHYTHMDB_QUERY_DISJUNCTION:
1576
1486
}
1577
1487
 
1578
1488
static void
1579
 
do_conjunction (RhythmDBTreeEntry *entry, gpointer unused,
 
1489
do_conjunction (RhythmDBEntry *entry, gpointer unused,
1580
1490
                struct RhythmDBTreeTraversalData *data)
1581
1491
{
1582
1492
        if (G_UNLIKELY (*data->cancel))
1633
1543
        if (album_query_idx >= 0) {
1634
1544
                RhythmDBTreeProperty *album;
1635
1545
                RhythmDBQueryData *qdata = g_ptr_array_index (data->query, album_query_idx);
 
1546
                RBRefString *albumname = rb_refstring_new (g_value_get_string (qdata->val));
1636
1547
                GPtrArray *oldquery = data->query;
1637
1548
 
1638
1549
                data->query = clone_remove_ptr_array_index (data->query, album_query_idx);
1639
1550
                
1640
 
                album = g_hash_table_lookup (artist->children, g_value_get_string (qdata->val));
 
1551
                album = g_hash_table_lookup (artist->children, albumname);
1641
1552
 
1642
1553
                if (album != NULL) {
1643
 
                                conjunctive_query_songs (album->name, album, data);
 
1554
                        conjunctive_query_songs (rb_refstring_get (albumname), album, data);
1644
1555
                }
1645
1556
                g_ptr_array_free (data->query, TRUE);
1646
1557
                data->query = oldquery;
1674
1585
        if (artist_query_idx >= 0) {
1675
1586
                RhythmDBTreeProperty *artist;
1676
1587
                RhythmDBQueryData *qdata = g_ptr_array_index (data->query, artist_query_idx);
 
1588
                RBRefString *artistname = rb_refstring_new (g_value_get_string (qdata->val));
1677
1589
                GPtrArray *oldquery = data->query;
1678
1590
 
1679
1591
                data->query = clone_remove_ptr_array_index (data->query, artist_query_idx);
1680
1592
                
1681
 
                artist = g_hash_table_lookup (genre->children, g_value_get_string (qdata->val));
 
1593
                artist = g_hash_table_lookup (genre->children, artistname);
1682
1594
                if (artist != NULL) {
1683
 
                        conjunctive_query_albums (artist->name, artist, data);
 
1595
                        conjunctive_query_albums (rb_refstring_get (artistname), artist, data);
1684
1596
                }
1685
1597
                g_ptr_array_free (data->query, TRUE);
1686
1598
                data->query = oldquery;
1717
1629
        if (genre_query_idx >= 0) {
1718
1630
                RhythmDBTreeProperty *genre;
1719
1631
                RhythmDBQueryData *qdata = g_ptr_array_index (data->query, genre_query_idx);
 
1632
                RBRefString *genrename = rb_refstring_new (g_value_get_string (qdata->val));
1720
1633
                GPtrArray *oldquery = data->query;
1721
1634
 
1722
1635
                data->query = clone_remove_ptr_array_index (data->query, genre_query_idx);
1723
1636
                
1724
 
                genre = g_hash_table_lookup (genres, g_value_get_string (qdata->val));
 
1637
                genre = g_hash_table_lookup (genres, genrename);
1725
1638
                if (genre != NULL) {
1726
 
                        conjunctive_query_artists (genre->name, genre, data);
 
1639
                        conjunctive_query_artists (rb_refstring_get (genrename), genre, data);
1727
1640
                } 
1728
1641
                g_ptr_array_free (data->query, TRUE);
1729
1642
                data->query = oldquery;
1767
1680
                
1768
1681
                g_ptr_array_remove_index_fast (query, type_query_idx);
1769
1682
                
1770
 
                etype = g_value_get_int (qdata->val);
 
1683
                etype = g_value_get_ulong (qdata->val);
1771
1684
                genres = get_genres_hash_for_type (db, etype);
1772
1685
                if (genres != NULL) {
1773
1686
                        conjunctive_query_genre (db, genres, traversal_data);
1811
1724
        for (i = last_disjunction; i < query->len; i++) {
1812
1725
                g_ptr_array_add (subquery, g_ptr_array_index (query, i));
1813
1726
        }
1814
 
        g_assert (subquery->len > 0);
1815
 
        
1816
 
        conjunctions = g_list_prepend (conjunctions, subquery);
 
1727
        
 
1728
        if (subquery->len > 0)
 
1729
                conjunctions = g_list_prepend (conjunctions, subquery);
 
1730
        
1817
1731
        return conjunctions;
1818
1732
}
1819
1733
 
1855
1769
}
1856
1770
 
1857
1771
static void
1858
 
handle_entry_match (RhythmDB *db, RhythmDBTreeEntry *entry,
 
1772
handle_entry_match (RhythmDB *db, RhythmDBEntry *entry,
1859
1773
                    struct RhythmDBTreeQueryGatheringData *data)
1860
1774
{
1861
1775
 
1896
1810
        return g_hash_table_lookup (db->priv->entries, uri);
1897
1811
}
1898
1812
 
1899
 
 
 
1813
struct RhythmDBEntryForeachCtxt
 
1814
{
 
1815
        RhythmDBTree *db;
 
1816
        GFunc func;
 
1817
        gpointer user_data;
 
1818
};
 
1819
 
 
1820
static void
 
1821
rhythmdb_tree_entry_foreach_func (gpointer key, gpointer val, gpointer data)
 
1822
{
 
1823
        struct RhythmDBEntryForeachCtxt * ctx = data;
 
1824
        ctx->func (val, ctx->user_data);
 
1825
}
 
1826
 
 
1827
static void
 
1828
rhythmdb_tree_entry_foreach (RhythmDB *adb, GFunc func, gpointer user_data)
 
1829
{
 
1830
        RhythmDBTree *db = RHYTHMDB_TREE (adb);
 
1831
        struct RhythmDBEntryForeachCtxt *ctx = g_new0 (struct RhythmDBEntryForeachCtxt, 1);
 
1832
        ctx->db = db;
 
1833
        ctx->func = func;
 
1834
        ctx->user_data = user_data;
 
1835
        g_hash_table_foreach (db->priv->entries, 
 
1836
                              (GHFunc) rhythmdb_tree_entry_foreach_func, ctx);
 
1837
        g_free (ctx);
 
1838
}
1900
1839
 
1901
1840
struct HashTreeIteratorCtxt {
1902
1841
        RhythmDBTree *db;
1910
1849
static void
1911
1850
hash_tree_entries_foreach (gpointer key, gpointer value, gpointer data)
1912
1851
{
1913
 
        RhythmDBTreeEntry *entry = (RhythmDBTreeEntry *)key;
 
1852
        RhythmDBEntry *entry = (RhythmDBEntry *) key;
1914
1853
        struct HashTreeIteratorCtxt *ctxt = (struct HashTreeIteratorCtxt*)data;
1915
1854
 
1916
1855
        g_assert (ctxt->entry_func);