00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include <stdio.h>
00023 #include <unistd.h>
00024 #include <stdlib.h>
00025 #include <string.h>
00026 #include <glib.h>
00027 #include <math.h>
00028
00029 #include "xmmspriv/xmms_collection.h"
00030 #include "xmmspriv/xmms_playlist.h"
00031 #include "xmmspriv/xmms_collquery.h"
00032 #include "xmmspriv/xmms_collserial.h"
00033 #include "xmmspriv/xmms_collsync.h"
00034 #include "xmmspriv/xmms_xform.h"
00035 #include "xmmspriv/xmms_streamtype.h"
00036 #include "xmms/xmms_ipc.h"
00037 #include "xmms/xmms_config.h"
00038 #include "xmms/xmms_log.h"
00039
00040
00041
00042
00043 typedef struct {
00044 const gchar *name;
00045 const gchar *namespace;
00046 xmmsv_coll_t *oldtarget;
00047 xmmsv_coll_t *newtarget;
00048 } coll_rebind_infos_t;
00049
00050 typedef struct {
00051 const gchar* oldname;
00052 const gchar* newname;
00053 const gchar* namespace;
00054 } coll_rename_infos_t;
00055
00056 typedef struct {
00057 xmms_coll_dag_t *dag;
00058 FuncApplyToColl func;
00059 void *udata;
00060 } coll_call_infos_t;
00061
00062 typedef struct {
00063 const gchar *target_name;
00064 const gchar *target_namespace;
00065 gboolean found;
00066 } coll_refcheck_t;
00067
00068 typedef struct {
00069 const gchar *key;
00070 xmmsv_coll_t *value;
00071 } coll_table_pair_t;
00072
00073 typedef enum {
00074 XMMS_COLLECTION_FIND_STATE_UNCHECKED,
00075 XMMS_COLLECTION_FIND_STATE_MATCH,
00076 XMMS_COLLECTION_FIND_STATE_NOMATCH,
00077 } coll_find_state_t;
00078
00079 typedef struct add_metadata_from_tree_user_data_St {
00080 xmms_medialib_session_t *session;
00081 xmms_medialib_entry_t entry;
00082 guint src;
00083 } add_metadata_from_tree_user_data_t;
00084
00085 static GList *global_stream_type;
00086
00087
00088
00089 static void xmms_collection_destroy (xmms_object_t *object);
00090
00091 static gboolean xmms_collection_validate (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *save_name, const gchar *save_namespace);
00092 static gboolean xmms_collection_validate_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *save_name, const gchar *save_namespace);
00093 static gboolean xmms_collection_unreference (xmms_coll_dag_t *dag, const gchar *name, guint nsid);
00094
00095 static gboolean xmms_collection_has_reference_to (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *tg_name, const gchar *tg_ns);
00096
00097 static void xmms_collection_apply_to_collection_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, FuncApplyToColl f, void *udata);
00098
00099 static void call_apply_to_coll (gpointer name, gpointer coll, gpointer udata);
00100 static void prepend_key_string (gpointer key, gpointer value, gpointer udata);
00101 static gboolean value_match_save_key (gpointer key, gpointer val, gpointer udata);
00102
00103 static void rebind_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
00104 static void rename_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
00105 static void strip_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
00106 static void check_for_reference (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
00107
00108 static void coll_unref (void *coll);
00109
00110 static GHashTable *xmms_collection_media_info (guint mid, xmms_error_t *err);
00111
00112 static gboolean filter_get_mediainfo_field_string (xmmsv_coll_t *coll, GHashTable *mediainfo, gchar **val);
00113 static gboolean filter_get_mediainfo_field_int (xmmsv_coll_t *coll, GHashTable *mediainfo, gint *val);
00114 static gboolean filter_get_operator_value_string (xmmsv_coll_t *coll, const gchar **val);
00115 static gboolean filter_get_operator_value_int (xmmsv_coll_t *coll, gint *val);
00116 static gboolean filter_get_operator_case (xmmsv_coll_t *coll, gboolean *val);
00117
00118 static void build_match_table (gpointer key, gpointer value, gpointer udata);
00119 static gboolean find_unchecked (gpointer name, gpointer value, gpointer udata);
00120 static void build_list_matches (gpointer key, gpointer value, gpointer udata);
00121
00122 static gboolean xmms_collection_media_match (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00123 static gboolean xmms_collection_media_match_operand (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00124 static gboolean xmms_collection_media_match_reference (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table, const gchar *refname, const gchar *refns);
00125 static gboolean xmms_collection_media_filter_has (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00126 static gboolean xmms_collection_media_filter_equals (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00127 static gboolean xmms_collection_media_filter_match (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00128 static gboolean xmms_collection_media_filter_smaller (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00129 static gboolean xmms_collection_media_filter_greater (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
00130 static xmmsv_coll_t *xmms_collection_idlist_from_pls (xmms_coll_dag_t *dag, const gchar *mediainfo, xmms_error_t *err);
00131
00132
00133 XMMS_CMD_DEFINE (collection_get, xmms_collection_get, xmms_coll_dag_t *, COLL, STRING, STRING);
00134 XMMS_CMD_DEFINE (collection_list, xmms_collection_list, xmms_coll_dag_t *, LIST, STRING, NONE);
00135 XMMS_CMD_DEFINE3 (collection_save, xmms_collection_save, xmms_coll_dag_t *, NONE, STRING, STRING, COLL);
00136 XMMS_CMD_DEFINE (collection_remove, xmms_collection_remove, xmms_coll_dag_t *, NONE, STRING, STRING);
00137 XMMS_CMD_DEFINE (collection_find, xmms_collection_find, xmms_coll_dag_t *, LIST, INT32, STRING);
00138 XMMS_CMD_DEFINE3 (collection_rename, xmms_collection_rename, xmms_coll_dag_t *, NONE, STRING, STRING, STRING);
00139 XMMS_CMD_DEFINE (collection_from_pls, xmms_collection_idlist_from_pls, xmms_coll_dag_t *, COLL, STRING, NONE);
00140 XMMS_CMD_DEFINE (collection_sync, xmms_collection_sync, xmms_coll_dag_t *, NONE, NONE, NONE);
00141
00142
00143 XMMS_CMD_DEFINE4 (query_ids, xmms_collection_query_ids, xmms_coll_dag_t *, LIST, COLL, INT32, INT32, LIST);
00144 XMMS_CMD_DEFINE6 (query_infos, xmms_collection_query_infos, xmms_coll_dag_t *, LIST, COLL, INT32, INT32, LIST, LIST, LIST);
00145
00146
00147 GTree *
00148 xmms_collection_changed_msg_new (xmms_collection_changed_actions_t type,
00149 const gchar *plname, const gchar *namespace)
00150 {
00151 GTree *dict;
00152
00153 dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
00154 NULL, (GDestroyNotify)xmmsv_unref);
00155
00156 g_tree_insert (dict, (gpointer) "type", xmmsv_new_int (type));
00157 g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (plname));
00158 g_tree_insert (dict, (gpointer) "namespace", xmmsv_new_string (namespace));
00159
00160 return dict;
00161 }
00162
00163 void
00164 xmms_collection_changed_msg_send (xmms_coll_dag_t *colldag, GTree *dict)
00165 {
00166 g_return_if_fail (colldag);
00167 g_return_if_fail (dict);
00168
00169 xmms_object_emit_f (XMMS_OBJECT (colldag),
00170 XMMS_IPC_SIGNAL_COLLECTION_CHANGED,
00171 XMMSV_TYPE_DICT,
00172 dict);
00173
00174 g_tree_destroy (dict);
00175 }
00176
00177 #define XMMS_COLLECTION_CHANGED_MSG(type, name, namespace) xmms_collection_changed_msg_send (dag, xmms_collection_changed_msg_new (type, name, namespace))
00178
00179
00180
00181
00182
00183
00184
00185
00186
00187
00188
00189
00190
00191
00192 struct xmms_coll_dag_St {
00193 xmms_object_t object;
00194
00195
00196 xmms_playlist_t *playlist;
00197
00198 GHashTable *collrefs[XMMS_COLLECTION_NUM_NAMESPACES];
00199
00200 GMutex *mutex;
00201
00202 };
00203
00204 static void
00205 coll_sync_cb (xmms_object_t *object, xmmsv_t *val, gpointer udata)
00206 {
00207 xmms_coll_sync_schedule_sync ();
00208 }
00209
00210
00211
00212
00213
00214 xmms_coll_dag_t *
00215 xmms_collection_init (xmms_playlist_t *playlist)
00216 {
00217 gint i;
00218 xmms_coll_dag_t *ret;
00219 xmms_stream_type_t *f;
00220
00221 ret = xmms_object_new (xmms_coll_dag_t, xmms_collection_destroy);
00222 ret->mutex = g_mutex_new ();
00223 ret->playlist = playlist;
00224
00225 xmms_coll_sync_init (ret);
00226
00227 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
00228 ret->collrefs[i] = g_hash_table_new_full (g_str_hash, g_str_equal,
00229 g_free, coll_unref);
00230 }
00231
00232 xmms_ipc_object_register (XMMS_IPC_OBJECT_COLLECTION, XMMS_OBJECT (ret));
00233
00234 xmms_ipc_broadcast_register (XMMS_OBJECT (ret),
00235 XMMS_IPC_SIGNAL_COLLECTION_CHANGED);
00236
00237
00238 xmms_object_connect (XMMS_OBJECT (ret),
00239 XMMS_IPC_SIGNAL_COLLECTION_CHANGED,
00240 coll_sync_cb, ret);
00241
00242
00243 xmms_object_connect (XMMS_OBJECT (playlist),
00244 XMMS_IPC_SIGNAL_PLAYLIST_CHANGED,
00245 coll_sync_cb, ret);
00246
00247 xmms_object_connect (XMMS_OBJECT (playlist),
00248 XMMS_IPC_SIGNAL_PLAYLIST_CURRENT_POS,
00249 coll_sync_cb, ret);
00250
00251 xmms_object_connect (XMMS_OBJECT (playlist),
00252 XMMS_IPC_SIGNAL_PLAYLIST_LOADED,
00253 coll_sync_cb, ret);
00254
00255
00256 xmms_object_cmd_add (XMMS_OBJECT (ret),
00257 XMMS_IPC_CMD_COLLECTION_GET,
00258 XMMS_CMD_FUNC (collection_get));
00259
00260 xmms_object_cmd_add (XMMS_OBJECT (ret),
00261 XMMS_IPC_CMD_COLLECTION_LIST,
00262 XMMS_CMD_FUNC (collection_list));
00263
00264 xmms_object_cmd_add (XMMS_OBJECT (ret),
00265 XMMS_IPC_CMD_COLLECTION_SAVE,
00266 XMMS_CMD_FUNC (collection_save));
00267
00268 xmms_object_cmd_add (XMMS_OBJECT (ret),
00269 XMMS_IPC_CMD_COLLECTION_REMOVE,
00270 XMMS_CMD_FUNC (collection_remove));
00271
00272 xmms_object_cmd_add (XMMS_OBJECT (ret),
00273 XMMS_IPC_CMD_COLLECTION_FIND,
00274 XMMS_CMD_FUNC (collection_find));
00275
00276 xmms_object_cmd_add (XMMS_OBJECT (ret),
00277 XMMS_IPC_CMD_COLLECTION_RENAME,
00278 XMMS_CMD_FUNC (collection_rename));
00279
00280 xmms_object_cmd_add (XMMS_OBJECT (ret),
00281 XMMS_IPC_CMD_QUERY_IDS,
00282 XMMS_CMD_FUNC (query_ids));
00283
00284 xmms_object_cmd_add (XMMS_OBJECT (ret),
00285 XMMS_IPC_CMD_QUERY_INFOS,
00286 XMMS_CMD_FUNC (query_infos));
00287
00288 xmms_object_cmd_add (XMMS_OBJECT (ret),
00289 XMMS_IPC_CMD_IDLIST_FROM_PLS,
00290 XMMS_CMD_FUNC (collection_from_pls));
00291
00292 xmms_object_cmd_add (XMMS_OBJECT (ret),
00293 XMMS_IPC_CMD_COLLECTION_SYNC,
00294 XMMS_CMD_FUNC (collection_sync));
00295
00296 xmms_collection_dag_restore (ret);
00297
00298 f = _xmms_stream_type_new (NULL,
00299 XMMS_STREAM_TYPE_MIMETYPE,
00300 "application/x-xmms2-playlist-entries",
00301 XMMS_STREAM_TYPE_END);
00302 global_stream_type = g_list_prepend (NULL, f);
00303
00304 return ret;
00305 }
00306
00307 static void
00308 add_metadata_from_tree (const gchar *key, xmmsv_t *value, gpointer user_data)
00309 {
00310 add_metadata_from_tree_user_data_t *ud = user_data;
00311
00312 if (xmmsv_get_type (value) == XMMSV_TYPE_INT32) {
00313 gint iv;
00314 xmmsv_get_int (value, &iv);
00315 xmms_medialib_entry_property_set_int_source (ud->session, ud->entry,
00316 key,
00317 iv,
00318 ud->src);
00319 } else if (xmmsv_get_type (value) == XMMSV_TYPE_STRING) {
00320 const gchar *sv;
00321 xmmsv_get_string (value, &sv);
00322 xmms_medialib_entry_property_set_str_source (ud->session, ud->entry,
00323 key,
00324 sv,
00325 ud->src);
00326 }
00327 }
00328
00329
00330
00331
00332
00333
00334
00335
00336 static xmmsv_coll_t *
00337 xmms_collection_idlist_from_pls (xmms_coll_dag_t *dag, const gchar *path,
00338 xmms_error_t *err)
00339 {
00340 xmms_xform_t *xform;
00341 GList *lst, *n;
00342 xmmsv_coll_t *coll;
00343 xmms_medialib_session_t *session;
00344 guint src;
00345 const gchar *buf;
00346
00347
00348 xform = xmms_xform_chain_setup_url (0, path, global_stream_type, TRUE);
00349
00350 if (!xform) {
00351 xmms_error_set (err, XMMS_ERROR_NO_SAUSAGE, "We can't handle this type of playlist or URL");
00352 return NULL;
00353 }
00354
00355 lst = xmms_xform_browse_method (xform, "/", err);
00356 if (xmms_error_iserror (err)) {
00357 xmms_object_unref (xform);
00358 return NULL;
00359 }
00360
00361 coll = xmmsv_coll_new (XMMS_COLLECTION_TYPE_IDLIST);
00362 session = xmms_medialib_begin_write ();
00363 src = xmms_medialib_source_to_id (session, "plugin/playlist");
00364
00365 n = lst;
00366 while (n) {
00367 xmms_medialib_entry_t entry;
00368
00369 xmmsv_t *a = n->data;
00370 xmmsv_t *b;
00371
00372 if (!xmmsv_dict_get (a, "realpath", &b)) {
00373 xmms_log_error ("Playlist plugin did not set realpath; probably a bug in plugin");
00374 xmmsv_unref (a);
00375 n = g_list_delete_link (n, n);
00376 continue;
00377 }
00378
00379 xmmsv_get_string (b, &buf);
00380 entry = xmms_medialib_entry_new_encoded (session, buf, err);
00381 xmmsv_dict_remove (a, "realpath");
00382 xmmsv_dict_remove (a, "path");
00383
00384 if (entry) {
00385 add_metadata_from_tree_user_data_t udata;
00386 udata.session = session;
00387 udata.entry = entry;
00388 udata.src = src;
00389
00390 xmmsv_dict_foreach(a, add_metadata_from_tree, &udata);
00391
00392 xmmsv_coll_idlist_append (coll, entry);
00393 } else {
00394 xmmsv_get_string (b, &buf);
00395 xmms_log_error ("couldn't add %s to collection!", buf);
00396 }
00397
00398 xmmsv_unref (a);
00399 n = g_list_delete_link (n, n);
00400 }
00401
00402 xmms_medialib_end (session);
00403 xmms_object_unref (xform);
00404
00405 return coll;
00406 }
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418 gboolean
00419 xmms_collection_remove (xmms_coll_dag_t *dag, const gchar *name,
00420 const gchar *namespace, xmms_error_t *err)
00421 {
00422 guint nsid;
00423 gboolean retval = FALSE;
00424 guint i;
00425
00426 nsid = xmms_collection_get_namespace_id (namespace);
00427 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00428 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00429 return FALSE;
00430 }
00431
00432 g_mutex_lock (dag->mutex);
00433
00434
00435 if (nsid == XMMS_COLLECTION_NSID_ALL) {
00436 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
00437 retval = xmms_collection_unreference (dag, name, i) || retval;
00438 }
00439 } else {
00440 retval = xmms_collection_unreference (dag, name, nsid);
00441 }
00442
00443 g_mutex_unlock (dag->mutex);
00444
00445 if (retval == FALSE) {
00446 xmms_error_set (err, XMMS_ERROR_NOENT, "Failed to remove this collection!");
00447 }
00448
00449 return retval;
00450 }
00451
00452
00453
00454
00455
00456
00457
00458
00459
00460
00461 gboolean
00462 xmms_collection_save (xmms_coll_dag_t *dag, const gchar *name, const gchar *namespace,
00463 xmmsv_coll_t *coll, xmms_error_t *err)
00464 {
00465 xmmsv_coll_t *existing;
00466 guint nsid;
00467 const gchar *alias;
00468 gchar *newkey = NULL;
00469
00470 nsid = xmms_collection_get_namespace_id (namespace);
00471 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00472 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00473 return FALSE;
00474 } else if (nsid == XMMS_COLLECTION_NSID_ALL) {
00475 xmms_error_set (err, XMMS_ERROR_GENERIC, "cannot save collection in all namespaces");
00476 return FALSE;
00477 }
00478
00479
00480 if (!xmms_collection_validate (dag, coll, name, namespace)) {
00481 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection structure");
00482 return FALSE;
00483 }
00484
00485 g_mutex_lock (dag->mutex);
00486
00487
00488 existing = xmms_collection_get_pointer (dag, name, nsid);
00489 if (existing != NULL) {
00490
00491 coll_rebind_infos_t infos = { name, namespace, existing, coll };
00492 xmms_collection_apply_to_all_collections (dag, rebind_references, &infos);
00493 }
00494
00495
00496 xmms_collection_apply_to_collection (dag, coll, bind_all_references, NULL);
00497
00498
00499 if (existing != NULL) {
00500 while ((alias = xmms_collection_find_alias (dag, nsid,
00501 existing, NULL)) != NULL) {
00502 newkey = g_strdup (alias);
00503
00504
00505 xmms_collection_dag_replace (dag, nsid, newkey, coll);
00506 xmmsv_coll_ref (coll);
00507
00508 XMMS_COLLECTION_CHANGED_MSG (XMMS_COLLECTION_CHANGED_UPDATE,
00509 newkey,
00510 namespace);
00511 }
00512
00513
00514 } else {
00515 newkey = g_strdup (name);
00516 xmms_collection_dag_replace (dag, nsid, newkey, coll);
00517 xmmsv_coll_ref (coll);
00518
00519 XMMS_COLLECTION_CHANGED_MSG (XMMS_COLLECTION_CHANGED_ADD,
00520 newkey,
00521 namespace);
00522 }
00523
00524 g_mutex_unlock (dag->mutex);
00525
00526
00527 if (nsid == XMMS_COLLECTION_NSID_PLAYLISTS) {
00528 XMMS_PLAYLIST_COLLECTION_CHANGED_MSG (dag->playlist, newkey);
00529 }
00530
00531 return TRUE;
00532 }
00533
00534
00535
00536
00537
00538
00539
00540
00541
00542
00543
00544
00545 xmmsv_coll_t *
00546 xmms_collection_get (xmms_coll_dag_t *dag, const gchar *name,
00547 const gchar *namespace, xmms_error_t *err)
00548 {
00549 xmmsv_coll_t *coll = NULL;
00550 guint nsid;
00551
00552 nsid = xmms_collection_get_namespace_id (namespace);
00553 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00554 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00555 return NULL;
00556 }
00557
00558 g_mutex_lock (dag->mutex);
00559
00560 coll = xmms_collection_get_pointer (dag, name, nsid);
00561
00562
00563 if (coll == NULL) {
00564 xmms_error_set (err, XMMS_ERROR_NOENT, "no such collection");
00565
00566
00567 } else {
00568 xmmsv_coll_ref (coll);
00569 }
00570
00571 g_mutex_unlock (dag->mutex);
00572
00573 return coll;
00574 }
00575
00576
00577
00578
00579
00580
00581
00582 void
00583 xmms_collection_sync (xmms_coll_dag_t *dag, xmms_error_t *err)
00584 {
00585 g_return_if_fail (dag);
00586
00587 g_mutex_lock (dag->mutex);
00588
00589 xmms_collection_dag_save (dag);
00590
00591 g_mutex_unlock (dag->mutex);
00592 }
00593
00594
00595
00596
00597
00598
00599
00600
00601
00602
00603
00604 GList *
00605 xmms_collection_list (xmms_coll_dag_t *dag, const gchar *namespace,
00606 xmms_error_t *err)
00607 {
00608 GList *r = NULL;
00609 guint nsid;
00610
00611 nsid = xmms_collection_get_namespace_id (namespace);
00612 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00613 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00614 return NULL;
00615 }
00616
00617 g_mutex_lock (dag->mutex);
00618
00619
00620 xmms_collection_foreach_in_namespace (dag, nsid, prepend_key_string, &r);
00621
00622 g_mutex_unlock (dag->mutex);
00623
00624 return r;
00625 }
00626
00627
00628
00629
00630
00631
00632
00633
00634
00635
00636 GList *
00637 xmms_collection_find (xmms_coll_dag_t *dag, guint mid, const gchar *namespace,
00638 xmms_error_t *err)
00639 {
00640 GHashTable *mediainfo;
00641 GList *ret = NULL;
00642 guint nsid;
00643 gchar *open_name;
00644 GHashTable *match_table;
00645 xmmsv_coll_t *coll;
00646
00647
00648 nsid = xmms_collection_get_namespace_id (namespace);
00649 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00650 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00651 return NULL;
00652 }
00653 if (nsid == XMMS_COLLECTION_NSID_ALL) {
00654 xmms_error_set (err, XMMS_ERROR_INVAL, "cannot search in all namespaces");
00655 return NULL;
00656 }
00657
00658
00659 match_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
00660 xmms_collection_foreach_in_namespace (dag, nsid, build_match_table, match_table);
00661
00662
00663 mediainfo = xmms_collection_media_info (mid, err);
00664
00665
00666 while (g_hash_table_find (match_table, find_unchecked, &open_name) != NULL) {
00667 coll_find_state_t *match = g_new (coll_find_state_t, 1);
00668 coll = xmms_collection_get_pointer (dag, open_name, nsid);
00669 if (xmms_collection_media_match (dag, mediainfo, coll, nsid, match_table)) {
00670 *match = XMMS_COLLECTION_FIND_STATE_MATCH;
00671 } else {
00672 *match = XMMS_COLLECTION_FIND_STATE_NOMATCH;
00673 }
00674 g_hash_table_replace (match_table, g_strdup (open_name), match);
00675 }
00676
00677
00678 g_hash_table_foreach (match_table, build_list_matches, &ret);
00679 g_hash_table_destroy (match_table);
00680
00681 g_hash_table_destroy (mediainfo);
00682
00683 return ret;
00684 }
00685
00686
00687
00688
00689
00690
00691
00692
00693
00694
00695
00696 gboolean xmms_collection_rename (xmms_coll_dag_t *dag, const gchar *from_name,
00697 const gchar *to_name, const gchar *namespace,
00698 xmms_error_t *err)
00699 {
00700 gboolean retval;
00701 guint nsid;
00702 xmmsv_coll_t *from_coll, *to_coll;
00703
00704 nsid = xmms_collection_get_namespace_id (namespace);
00705 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
00706 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
00707 return FALSE;
00708 } else if (nsid == XMMS_COLLECTION_NSID_ALL) {
00709 xmms_error_set (err, XMMS_ERROR_GENERIC, "cannot rename collection in all namespaces");
00710 return FALSE;
00711 }
00712
00713 g_mutex_lock (dag->mutex);
00714
00715 from_coll = xmms_collection_get_pointer (dag, from_name, nsid);
00716 to_coll = xmms_collection_get_pointer (dag, to_name, nsid);
00717
00718
00719 if (from_coll == NULL) {
00720 xmms_error_set (err, XMMS_ERROR_NOENT, "no such collection");
00721 retval = FALSE;
00722
00723 } else if (to_coll != NULL) {
00724 xmms_error_set (err, XMMS_ERROR_NOENT, "a collection already exists with the target name");
00725 retval = FALSE;
00726
00727
00728 } else {
00729 GTree *dict;
00730
00731
00732 xmms_collection_dag_replace (dag, nsid, g_strdup (to_name), from_coll);
00733 xmmsv_coll_ref (from_coll);
00734
00735
00736 g_hash_table_remove (dag->collrefs[nsid], from_name);
00737
00738
00739 coll_rename_infos_t infos = { from_name, to_name, namespace };
00740 xmms_collection_apply_to_all_collections (dag, rename_references, &infos);
00741
00742
00743 dict = xmms_collection_changed_msg_new (XMMS_COLLECTION_CHANGED_RENAME,
00744 from_name, namespace);
00745 g_tree_insert (dict, (gpointer) "newname", xmmsv_new_string (to_name));
00746 xmms_collection_changed_msg_send (dag, dict);
00747
00748 retval = TRUE;
00749 }
00750
00751 g_mutex_unlock (dag->mutex);
00752
00753 return retval;
00754 }
00755
00756
00757
00758
00759
00760
00761
00762
00763
00764
00765
00766
00767 GList *
00768 xmms_collection_query_ids (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
00769 guint lim_start, guint lim_len, xmmsv_t *order,
00770 xmms_error_t *err)
00771 {
00772 GList *res, *n;
00773 xmmsv_t *fetch, *group, *idval;
00774
00775
00776 group = xmmsv_new_list ();
00777 fetch = xmmsv_new_list ();
00778 idval = xmmsv_new_string ("id");
00779 xmmsv_list_append (fetch, idval);
00780
00781 res = xmms_collection_query_infos (dag, coll, lim_start, lim_len, order, fetch, group, err);
00782
00783
00784 for (n = res; n; n = n->next) {
00785 xmms_medialib_entry_t id;
00786 xmmsv_t *id_val, *cmdval = n->data;
00787
00788 xmmsv_dict_get (cmdval, "id", &id_val);
00789 xmmsv_get_int (id_val, &id);
00790 n->data = xmmsv_new_int (id);
00791
00792 xmmsv_unref (cmdval);
00793 }
00794
00795 xmmsv_unref (group);
00796 xmmsv_unref (fetch);
00797 xmmsv_unref (idval);
00798
00799 return res;
00800 }
00801
00802
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812
00813
00814
00815 GList *
00816 xmms_collection_query_infos (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
00817 guint lim_start, guint lim_len, xmmsv_t *order,
00818 xmmsv_t *fetch, xmmsv_t *group, xmms_error_t *err)
00819 {
00820 GList *res = NULL;
00821 GString *query;
00822
00823
00824 if (xmmsv_list_get_size (fetch) == 0) {
00825 xmms_error_set (err, XMMS_ERROR_INVAL, "fetch list must not be empty!");
00826 return NULL;
00827 }
00828
00829
00830 if (!check_string_list (order)) {
00831 xmms_error_set (err, XMMS_ERROR_NOENT, "invalid order list!");
00832 return NULL;
00833 }
00834 if (!check_string_list (fetch)) {
00835 xmms_error_set (err, XMMS_ERROR_NOENT, "invalid fetch list!");
00836 return NULL;
00837 }
00838 if (!check_string_list (group)) {
00839 xmms_error_set (err, XMMS_ERROR_NOENT, "invalid group list!");
00840 return NULL;
00841 }
00842
00843
00844 if (!xmms_collection_validate (dag, coll, NULL, NULL)) {
00845 if (err) {
00846 xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection structure");
00847 }
00848 return NULL;
00849 }
00850
00851 g_mutex_lock (dag->mutex);
00852
00853 query = xmms_collection_get_query (dag, coll, lim_start, lim_len,
00854 order, fetch, group);
00855
00856 g_mutex_unlock (dag->mutex);
00857
00858 XMMS_DBG ("COLLECTIONS: query_infos with %s", query->str);
00859
00860
00861 xmms_medialib_session_t *session = xmms_medialib_begin ();
00862 res = xmms_medialib_select (session, query->str, err);
00863 xmms_medialib_end (session);
00864
00865 g_string_free (query, TRUE);
00866
00867 return res;
00868 }
00869
00870
00871
00872
00873
00874
00875
00876
00877
00878 void
00879 xmms_collection_update_pointer (xmms_coll_dag_t *dag, const gchar *name,
00880 guint nsid, xmmsv_coll_t *newtarget)
00881 {
00882 xmms_collection_dag_replace (dag, nsid, g_strdup (name), newtarget);
00883 xmmsv_coll_ref (newtarget);
00884 }
00885
00886
00887 void
00888 xmms_collection_dag_replace (xmms_coll_dag_t *dag,
00889 xmms_collection_namespace_id_t nsid,
00890 gchar *key, xmmsv_coll_t *newcoll)
00891 {
00892 g_hash_table_replace (dag->collrefs[nsid], key, newcoll);
00893 }
00894
00895
00896
00897
00898
00899
00900
00901
00902 xmmsv_coll_t *
00903 xmms_collection_get_pointer (xmms_coll_dag_t *dag, const gchar *collname,
00904 guint nsid)
00905 {
00906 gint i;
00907 xmmsv_coll_t *coll = NULL;
00908
00909 if (nsid == XMMS_COLLECTION_NSID_ALL) {
00910 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES && coll == NULL; ++i) {
00911 coll = g_hash_table_lookup (dag->collrefs[i], collname);
00912 }
00913 } else {
00914 coll = g_hash_table_lookup (dag->collrefs[nsid], collname);
00915 }
00916
00917 return coll;
00918 }
00919
00920
00921
00922
00923
00924
00925
00926
00927 gboolean
00928 xmms_collection_get_int_attr (xmmsv_coll_t *coll, const gchar *attrname, gint *val)
00929 {
00930 gboolean retval = FALSE;
00931 gint buf;
00932 gchar *str;
00933 gchar *endptr;
00934
00935 if (xmmsv_coll_attribute_get (coll, attrname, &str)) {
00936 buf = strtol (str, &endptr, 10);
00937
00938
00939 if (*endptr == '\0') {
00940 *val = buf;
00941 retval = TRUE;
00942 }
00943 }
00944
00945 return retval;
00946 }
00947
00948
00949
00950
00951
00952
00953
00954
00955 gboolean
00956 xmms_collection_set_int_attr (xmmsv_coll_t *coll, const gchar *attrname,
00957 gint newval)
00958 {
00959 gboolean retval = FALSE;
00960 gchar str[XMMS_MAX_INT_ATTRIBUTE_LEN + 1];
00961 gint written;
00962
00963 written = g_snprintf (str, sizeof (str), "%d", newval);
00964 if (written < XMMS_MAX_INT_ATTRIBUTE_LEN) {
00965 xmmsv_coll_attribute_set (coll, attrname, str);
00966 retval = TRUE;
00967 }
00968
00969 return retval;
00970 }
00971
00972
00973
00974
00975
00976
00977
00978
00979
00980
00981
00982
00983
00984 const gchar *
00985 xmms_collection_find_alias (xmms_coll_dag_t *dag, guint nsid,
00986 xmmsv_coll_t *value, const gchar *key)
00987 {
00988 const gchar *otherkey = NULL;
00989 coll_table_pair_t search_pair = { key, value };
00990
00991 if (g_hash_table_find (dag->collrefs[nsid], value_match_save_key,
00992 &search_pair) != NULL) {
00993 otherkey = search_pair.key;
00994 }
00995
00996 return otherkey;
00997 }
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007 xmms_medialib_entry_t
01008 xmms_collection_get_random_media (xmms_coll_dag_t *dag, xmmsv_coll_t *source)
01009 {
01010 GList *res;
01011 xmms_medialib_entry_t mid = 0;
01012 xmmsv_t *rorder = xmmsv_new_list ();
01013 xmmsv_t *randval = xmmsv_new_string ("~RANDOM()");
01014
01015
01016 xmmsv_list_append (rorder, randval);
01017
01018 res = xmms_collection_query_ids (dag, source, 0, 1, rorder, NULL);
01019
01020 if (res != NULL) {
01021 xmmsv_t *val = (xmmsv_t *) res->data;
01022 xmmsv_get_int (val, &mid);
01023 xmmsv_unref (val);
01024 g_list_free (res);
01025 }
01026
01027 xmmsv_unref (rorder);
01028 xmmsv_unref (randval);
01029
01030 return mid;
01031 }
01032
01033
01034
01035
01036
01037
01038
01039
01040
01041 static void
01042 xmms_collection_destroy (xmms_object_t *object)
01043 {
01044 gint i;
01045 xmms_coll_dag_t *dag = (xmms_coll_dag_t *)object;
01046
01047 g_return_if_fail (dag);
01048
01049 xmms_coll_sync_shutdown ();
01050 xmms_collection_dag_save (dag);
01051
01052 g_mutex_free (dag->mutex);
01053
01054 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
01055 g_hash_table_destroy (dag->collrefs[i]);
01056 }
01057
01058 xmms_ipc_broadcast_unregister (XMMS_IPC_SIGNAL_COLLECTION_CHANGED);
01059
01060 xmms_ipc_object_unregister (XMMS_IPC_OBJECT_COLLECTION);
01061 }
01062
01063
01064
01065
01066
01067
01068
01069
01070
01071
01072
01073 static gboolean
01074 xmms_collection_validate (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
01075 const gchar *save_name, const gchar *save_namespace)
01076 {
01077
01078 if (save_namespace != NULL &&
01079 strcmp (save_namespace, XMMS_COLLECTION_NS_PLAYLISTS) == 0) {
01080
01081 if (xmmsv_coll_get_type (coll) != XMMS_COLLECTION_TYPE_IDLIST &&
01082 xmmsv_coll_get_type (coll) != XMMS_COLLECTION_TYPE_QUEUE &&
01083 xmmsv_coll_get_type (coll) != XMMS_COLLECTION_TYPE_PARTYSHUFFLE) {
01084 return FALSE;
01085 }
01086 }
01087
01088
01089 return xmms_collection_validate_recurs (dag, coll, save_name,
01090 save_namespace);
01091 }
01092
01093
01094
01095
01096
01097 static gboolean
01098 xmms_collection_validate_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
01099 const gchar *save_name, const gchar *save_namespace)
01100 {
01101 guint num_operands = 0;
01102 xmmsv_coll_t *op, *ref;
01103 gchar *attr, *attr2;
01104 gboolean valid = TRUE;
01105 xmmsv_coll_type_t type;
01106 xmms_collection_namespace_id_t nsid;
01107
01108
01109 xmmsv_coll_operand_list_save (coll);
01110
01111 xmmsv_coll_operand_list_first (coll);
01112 while (xmmsv_coll_operand_list_entry (coll, &op)) {
01113 num_operands++;
01114 xmmsv_coll_operand_list_next (coll);
01115 }
01116
01117 xmmsv_coll_operand_list_restore (coll);
01118
01119
01120
01121 type = xmmsv_coll_get_type (coll);
01122 switch (type) {
01123 case XMMS_COLLECTION_TYPE_REFERENCE:
01124
01125 if (num_operands > 1) {
01126 return FALSE;
01127 }
01128
01129
01130 xmmsv_coll_attribute_get (coll, "reference", &attr);
01131 if (attr == NULL) {
01132 return FALSE;
01133 } else if (strcmp (attr, "All Media") != 0) {
01134 xmmsv_coll_attribute_get (coll, "namespace", &attr2);
01135
01136 if (attr2 == NULL) {
01137 return FALSE;
01138 }
01139
01140 nsid = xmms_collection_get_namespace_id (attr2);
01141 if (nsid == XMMS_COLLECTION_NSID_INVALID) {
01142 return FALSE;
01143 }
01144
01145 g_mutex_lock (dag->mutex);
01146 ref = xmms_collection_get_pointer (dag, attr, nsid);
01147 if (ref == NULL) {
01148 g_mutex_unlock (dag->mutex);
01149 return FALSE;
01150 }
01151
01152 if (save_name && save_namespace) {
01153
01154 if (strcmp (attr, save_name) == 0 &&
01155 strcmp (attr2, save_namespace) == 0) {
01156
01157 g_mutex_unlock (dag->mutex);
01158 return FALSE;
01159
01160
01161 } else if (xmms_collection_has_reference_to (dag, ref, save_name,
01162 save_namespace)) {
01163 g_mutex_unlock (dag->mutex);
01164 return FALSE;
01165 }
01166 }
01167
01168 g_mutex_unlock (dag->mutex);
01169 } else {
01170
01171 ref = NULL;
01172 }
01173
01174
01175 if (num_operands == 1) {
01176 xmmsv_coll_operand_list_save (coll);
01177 xmmsv_coll_operand_list_first (coll);
01178 xmmsv_coll_operand_list_entry (coll, &op);
01179 xmmsv_coll_operand_list_restore (coll);
01180
01181 if (op != ref) {
01182 return FALSE;
01183 }
01184 }
01185 break;
01186
01187 case XMMS_COLLECTION_TYPE_UNION:
01188 case XMMS_COLLECTION_TYPE_INTERSECTION:
01189
01190 if (num_operands == 0) {
01191 return FALSE;
01192 }
01193 break;
01194
01195 case XMMS_COLLECTION_TYPE_COMPLEMENT:
01196
01197 if (num_operands != 1) {
01198 return FALSE;
01199 }
01200 break;
01201
01202 case XMMS_COLLECTION_TYPE_HAS:
01203
01204 if (num_operands != 1) {
01205 return FALSE;
01206 }
01207
01208
01209
01210 if (!xmmsv_coll_attribute_get (coll, "field", &attr)) {
01211 return FALSE;
01212 }
01213 break;
01214
01215 case XMMS_COLLECTION_TYPE_EQUALS:
01216 case XMMS_COLLECTION_TYPE_MATCH:
01217 case XMMS_COLLECTION_TYPE_SMALLER:
01218 case XMMS_COLLECTION_TYPE_GREATER:
01219
01220 if (num_operands != 1) {
01221 return FALSE;
01222 }
01223
01224
01225
01226 if (!xmmsv_coll_attribute_get (coll, "field", &attr)) {
01227 return FALSE;
01228 }
01229
01230
01231
01232
01233
01234
01235 if (!xmmsv_coll_attribute_get (coll, "value", &attr)) {
01236 return FALSE;
01237 }
01238 break;
01239
01240 case XMMS_COLLECTION_TYPE_IDLIST:
01241 case XMMS_COLLECTION_TYPE_QUEUE:
01242
01243 if (num_operands > 0) {
01244 return FALSE;
01245 }
01246 break;
01247
01248 case XMMS_COLLECTION_TYPE_PARTYSHUFFLE:
01249
01250 if (num_operands != 1) {
01251 return FALSE;
01252 }
01253 break;
01254
01255
01256 default:
01257 return FALSE;
01258 break;
01259 }
01260
01261
01262
01263 if (num_operands > 0 && type != XMMS_COLLECTION_TYPE_REFERENCE) {
01264 xmmsv_coll_operand_list_save (coll);
01265
01266 xmmsv_coll_operand_list_first (coll);
01267 while (xmmsv_coll_operand_list_entry (coll, &op) && valid) {
01268 if (!xmms_collection_validate_recurs (dag, op, save_name,
01269 save_namespace)) {
01270 valid = FALSE;
01271 }
01272 xmmsv_coll_operand_list_next (coll);
01273 }
01274
01275 xmmsv_coll_operand_list_restore (coll);
01276 }
01277
01278 return valid;
01279 }
01280
01281
01282
01283
01284
01285
01286
01287
01288 static gboolean
01289 xmms_collection_unreference (xmms_coll_dag_t *dag, const gchar *name, guint nsid)
01290 {
01291 xmmsv_coll_t *existing, *active_pl;
01292 gboolean retval = FALSE;
01293
01294 existing = g_hash_table_lookup (dag->collrefs[nsid], name);
01295 active_pl = g_hash_table_lookup (dag->collrefs[XMMS_COLLECTION_NSID_PLAYLISTS],
01296 XMMS_ACTIVE_PLAYLIST);
01297
01298
01299 if (existing != NULL && existing != active_pl) {
01300 const gchar *matchkey;
01301 const gchar *nsname = xmms_collection_get_namespace_string (nsid);
01302 coll_rebind_infos_t infos = { name, nsname, existing, NULL };
01303
01304
01305
01306
01307
01308 xmms_collection_apply_to_all_collections (dag, strip_references, &infos);
01309
01310
01311 while ((matchkey = xmms_collection_find_alias (dag, nsid,
01312 existing, NULL)) != NULL) {
01313
01314 XMMS_COLLECTION_CHANGED_MSG (XMMS_COLLECTION_CHANGED_REMOVE,
01315 matchkey,
01316 nsname);
01317
01318 g_hash_table_remove (dag->collrefs[nsid], matchkey);
01319 }
01320
01321 retval = TRUE;
01322 }
01323
01324 return retval;
01325 }
01326
01327
01328
01329
01330
01331
01332 xmms_collection_namespace_id_t
01333 xmms_collection_get_namespace_id (const gchar *namespace)
01334 {
01335 guint nsid;
01336
01337 if (strcmp (namespace, XMMS_COLLECTION_NS_ALL) == 0) {
01338 nsid = XMMS_COLLECTION_NSID_ALL;
01339 } else if (strcmp (namespace, XMMS_COLLECTION_NS_COLLECTIONS) == 0) {
01340 nsid = XMMS_COLLECTION_NSID_COLLECTIONS;
01341 } else if (strcmp (namespace, XMMS_COLLECTION_NS_PLAYLISTS) == 0) {
01342 nsid = XMMS_COLLECTION_NSID_PLAYLISTS;
01343 } else {
01344 nsid = XMMS_COLLECTION_NSID_INVALID;
01345 }
01346
01347 return nsid;
01348 }
01349
01350
01351
01352
01353
01354
01355 const gchar *
01356 xmms_collection_get_namespace_string (xmms_collection_namespace_id_t nsid)
01357 {
01358 const gchar *name;
01359
01360 switch (nsid) {
01361 case XMMS_COLLECTION_NSID_ALL:
01362 name = XMMS_COLLECTION_NS_ALL;
01363 break;
01364 case XMMS_COLLECTION_NSID_COLLECTIONS:
01365 name = XMMS_COLLECTION_NS_COLLECTIONS;
01366 break;
01367 case XMMS_COLLECTION_NSID_PLAYLISTS:
01368 name = XMMS_COLLECTION_NS_PLAYLISTS;
01369 break;
01370
01371 case XMMS_COLLECTION_NSID_INVALID:
01372 default:
01373 name = NULL;
01374 break;
01375 }
01376
01377 return name;
01378 }
01379
01380
01381
01382
01383
01384
01385
01386
01387
01388
01389
01390 static gboolean
01391 xmms_collection_has_reference_to (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
01392 const gchar *tg_name, const gchar *tg_ns)
01393 {
01394 coll_refcheck_t check = { tg_name, tg_ns, FALSE };
01395 xmms_collection_apply_to_collection (dag, coll, check_for_reference, &check);
01396
01397 return check.found;
01398 }
01399
01400
01401
01402
01403
01404
01405
01406
01407
01408 void
01409 xmms_collection_foreach_in_namespace (xmms_coll_dag_t *dag, guint nsid, GHFunc f, void *udata)
01410 {
01411 gint i;
01412
01413 if (nsid == XMMS_COLLECTION_NSID_ALL) {
01414 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
01415 g_hash_table_foreach (dag->collrefs[i], f, udata);
01416 }
01417 } else if (nsid != XMMS_COLLECTION_NSID_INVALID) {
01418 g_hash_table_foreach (dag->collrefs[nsid], f, udata);
01419 }
01420 }
01421
01422
01423
01424
01425
01426
01427
01428 void
01429 xmms_collection_apply_to_all_collections (xmms_coll_dag_t *dag,
01430 FuncApplyToColl f, void *udata)
01431 {
01432 gint i;
01433 coll_call_infos_t callinfos = { dag, f, udata };
01434
01435 for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
01436 g_hash_table_foreach (dag->collrefs[i], call_apply_to_coll, &callinfos);
01437 }
01438 }
01439
01440
01441
01442
01443
01444
01445
01446
01447 void
01448 xmms_collection_apply_to_collection (xmms_coll_dag_t *dag,
01449 xmmsv_coll_t *coll,
01450 FuncApplyToColl f, void *udata)
01451 {
01452 xmms_collection_apply_to_collection_recurs (dag, coll, NULL, f, udata);
01453 }
01454
01455
01456 static void
01457 xmms_collection_apply_to_collection_recurs (xmms_coll_dag_t *dag,
01458 xmmsv_coll_t *coll,
01459 xmmsv_coll_t *parent,
01460 FuncApplyToColl f, void *udata)
01461 {
01462 xmmsv_coll_t *op;
01463
01464
01465 f (dag, coll, parent, udata);
01466
01467
01468 if (xmmsv_coll_get_type (coll) != XMMS_COLLECTION_TYPE_REFERENCE) {
01469 xmmsv_coll_operand_list_save (coll);
01470
01471 xmmsv_coll_operand_list_first (coll);
01472 while (xmmsv_coll_operand_list_entry (coll, &op)) {
01473 xmms_collection_apply_to_collection_recurs (dag, op, coll, f, udata);
01474 xmmsv_coll_operand_list_next (coll);
01475 }
01476
01477 xmmsv_coll_operand_list_restore (coll);
01478 }
01479 }
01480
01481
01482
01483
01484
01485 static void
01486 call_apply_to_coll (gpointer name, gpointer coll, gpointer udata)
01487 {
01488 coll_call_infos_t *callinfos = (coll_call_infos_t*)udata;
01489
01490 xmms_collection_apply_to_collection (callinfos->dag, coll,
01491 callinfos->func, callinfos->udata);
01492 }
01493
01494
01495
01496
01497 static void
01498 prepend_key_string (gpointer key, gpointer value, gpointer udata)
01499 {
01500 GList **list = (GList**)udata;
01501 *list = g_list_prepend (*list, xmmsv_new_string (key));
01502 }
01503
01504
01505
01506
01507
01508
01509 static gboolean
01510 value_match_save_key (gpointer key, gpointer val, gpointer udata)
01511 {
01512 gboolean found = FALSE;
01513 coll_table_pair_t *pair = (coll_table_pair_t*)udata;
01514 xmmsv_coll_t *coll = (xmmsv_coll_t*)val;
01515
01516
01517 if ((coll == pair->value) &&
01518 (pair->key == NULL || strcmp (pair->key, key) != 0)) {
01519 pair->key = key;
01520 found = TRUE;
01521 }
01522
01523 return found;
01524 }
01525
01526
01527
01528
01529
01530 void
01531 bind_all_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
01532 {
01533 if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE) {
01534 xmmsv_coll_t *target;
01535 gchar *target_name;
01536 gchar *target_namespace;
01537 gint target_nsid;
01538
01539 xmmsv_coll_attribute_get (coll, "reference", &target_name);
01540 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
01541 if (target_name == NULL || target_namespace == NULL ||
01542 strcmp (target_name, "All Media") == 0) {
01543 return;
01544 }
01545
01546 target_nsid = xmms_collection_get_namespace_id (target_namespace);
01547 if (target_nsid == XMMS_COLLECTION_NSID_INVALID) {
01548 return;
01549 }
01550
01551 target = xmms_collection_get_pointer (dag, target_name, target_nsid);
01552 if (target == NULL) {
01553 return;
01554 }
01555
01556 xmmsv_coll_add_operand (coll, target);
01557 }
01558 }
01559
01560
01561
01562
01563
01564
01565 static void
01566 rebind_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
01567 {
01568 if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE) {
01569 coll_rebind_infos_t *infos;
01570
01571 gchar *target_name = NULL;
01572 gchar *target_namespace = NULL;
01573
01574 infos = (coll_rebind_infos_t*)udata;
01575
01576
01577
01578 xmmsv_coll_attribute_get (coll, "reference", &target_name);
01579 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
01580 if (strcmp (infos->name, target_name) != 0 ||
01581 strcmp (infos->namespace, target_namespace) != 0) {
01582 return;
01583 }
01584
01585 xmmsv_coll_remove_operand (coll, infos->oldtarget);
01586 xmmsv_coll_add_operand (coll, infos->newtarget);
01587 }
01588 }
01589
01590
01591
01592
01593
01594 static void
01595 rename_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
01596 {
01597 if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE) {
01598 coll_rename_infos_t *infos;
01599
01600 gchar *target_name = NULL;
01601 gchar *target_namespace = NULL;
01602
01603 infos = (coll_rename_infos_t*)udata;
01604
01605 xmmsv_coll_attribute_get (coll, "reference", &target_name);
01606 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
01607 if (strcmp (infos->oldname, target_name) == 0 &&
01608 strcmp (infos->namespace, target_namespace) == 0) {
01609 xmmsv_coll_attribute_set (coll, "reference", infos->newname);
01610 }
01611 }
01612 }
01613
01614
01615
01616
01617
01618 static void
01619 strip_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
01620 {
01621 xmmsv_coll_t *op;
01622 coll_rebind_infos_t *infos;
01623 gchar *target_name = NULL;
01624 gchar *target_namespace = NULL;
01625
01626 infos = (coll_rebind_infos_t*)udata;
01627
01628 xmmsv_coll_operand_list_save (coll);
01629 xmmsv_coll_operand_list_first (coll);
01630 while (xmmsv_coll_operand_list_entry (coll, &op)) {
01631
01632 if (xmmsv_coll_get_type (op) != XMMS_COLLECTION_TYPE_REFERENCE) {
01633 xmmsv_coll_operand_list_next (coll);
01634 continue;
01635 }
01636
01637 xmmsv_coll_attribute_get (op, "reference", &target_name);
01638 xmmsv_coll_attribute_get (op, "namespace", &target_namespace);
01639 if (strcmp (infos->name, target_name) != 0 ||
01640 strcmp (infos->namespace, target_namespace) != 0) {
01641 xmmsv_coll_operand_list_next (coll);
01642 continue;
01643 }
01644
01645
01646 xmmsv_coll_remove_operand (op, infos->oldtarget);
01647
01648 xmmsv_coll_remove_operand (coll, op);
01649 xmmsv_coll_add_operand (coll, infos->oldtarget);
01650
01651 xmmsv_coll_operand_list_first (coll);
01652 }
01653 xmmsv_coll_operand_list_restore (coll);
01654 }
01655
01656
01657
01658
01659
01660 static void
01661 check_for_reference (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
01662 {
01663 coll_refcheck_t *check = (coll_refcheck_t*)udata;
01664 if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE && !check->found) {
01665 gchar *target_name, *target_namespace;
01666
01667 xmmsv_coll_attribute_get (coll, "reference", &target_name);
01668 xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
01669 if (strcmp (check->target_name, target_name) == 0 &&
01670 strcmp (check->target_namespace, target_namespace) == 0) {
01671 check->found = TRUE;
01672 } else {
01673 xmmsv_coll_t *op;
01674 xmmsv_coll_operand_list_save (coll);
01675 xmmsv_coll_operand_list_first (coll);
01676 if (xmmsv_coll_operand_list_entry (coll, &op)) {
01677 xmms_collection_apply_to_collection_recurs (dag, op, coll,
01678 check_for_reference,
01679 udata);
01680 }
01681 xmmsv_coll_operand_list_restore (coll);
01682 }
01683 }
01684 }
01685
01686
01687
01688
01689
01690
01691 static void
01692 coll_unref (void *coll)
01693 {
01694 xmmsv_coll_unref (coll);
01695 }
01696
01697
01698
01699
01700
01701
01702 static void
01703 build_match_table (gpointer key, gpointer value, gpointer udata)
01704 {
01705 GHashTable *match_table = udata;
01706 coll_find_state_t *match = g_new (coll_find_state_t, 1);
01707 *match = XMMS_COLLECTION_FIND_STATE_UNCHECKED;
01708 g_hash_table_replace (match_table, g_strdup (key), match);
01709 }
01710
01711
01712
01713
01714 static gboolean
01715 find_unchecked (gpointer name, gpointer value, gpointer udata)
01716 {
01717 coll_find_state_t *match = value;
01718 gchar **open = udata;
01719 *open = name;
01720 return (*match == XMMS_COLLECTION_FIND_STATE_UNCHECKED);
01721 }
01722
01723
01724
01725
01726 static void
01727 build_list_matches (gpointer key, gpointer value, gpointer udata)
01728 {
01729 gchar *coll_name = key;
01730 coll_find_state_t *state = value;
01731 GList **list = udata;
01732 if (*state == XMMS_COLLECTION_FIND_STATE_MATCH) {
01733 *list = g_list_prepend (*list, xmmsv_new_string (coll_name));
01734 }
01735 }
01736
01737
01738
01739
01740
01741
01742
01743
01744
01745
01746 static gboolean
01747 xmms_collection_media_match (xmms_coll_dag_t *dag, GHashTable *mediainfo,
01748 xmmsv_coll_t *coll, guint nsid,
01749 GHashTable *match_table)
01750 {
01751 gboolean match = FALSE;
01752 xmmsv_coll_t *op;
01753 gchar *attr1 = NULL, *attr2 = NULL;
01754 xmmsv_t *val;
01755 guint32 *idlist;
01756 gint i;
01757 gint id;
01758
01759 switch (xmmsv_coll_get_type (coll)) {
01760 case XMMS_COLLECTION_TYPE_REFERENCE:
01761 if (xmmsv_coll_attribute_get (coll, "reference", &attr1)) {
01762 if (strcmp (attr1, "All Media") == 0) {
01763 match = TRUE;
01764 } else if (xmmsv_coll_attribute_get (coll, "namespace", &attr2)) {
01765 match = xmms_collection_media_match_reference (dag, mediainfo,
01766 coll, nsid,
01767 match_table,
01768 attr1, attr2);
01769 }
01770 }
01771 break;
01772
01773 case XMMS_COLLECTION_TYPE_UNION:
01774
01775 xmmsv_coll_operand_list_save (coll);
01776 xmmsv_coll_operand_list_first (coll);
01777 while (!match && xmmsv_coll_operand_list_entry (coll, &op)) {
01778 match = xmms_collection_media_match (dag, mediainfo, op,
01779 nsid, match_table);
01780 xmmsv_coll_operand_list_next (coll);
01781 }
01782 xmmsv_coll_operand_list_restore (coll);
01783 break;
01784
01785 case XMMS_COLLECTION_TYPE_INTERSECTION:
01786
01787 match = TRUE;
01788 xmmsv_coll_operand_list_save (coll);
01789 xmmsv_coll_operand_list_first (coll);
01790 while (match && xmmsv_coll_operand_list_entry (coll, &op)) {
01791 match = xmms_collection_media_match (dag, mediainfo, op,
01792 nsid, match_table);
01793 xmmsv_coll_operand_list_next (coll);
01794 }
01795 xmmsv_coll_operand_list_restore (coll);
01796 break;
01797
01798 case XMMS_COLLECTION_TYPE_COMPLEMENT:
01799
01800 match = !xmms_collection_media_match_operand (dag, mediainfo, coll,
01801 nsid, match_table);
01802 break;
01803
01804 case XMMS_COLLECTION_TYPE_HAS:
01805 match = xmms_collection_media_filter_has (dag, mediainfo, coll,
01806 nsid, match_table);
01807 break;
01808
01809 case XMMS_COLLECTION_TYPE_EQUALS:
01810 match = xmms_collection_media_filter_equals (dag, mediainfo, coll,
01811 nsid, match_table);
01812 break;
01813
01814 case XMMS_COLLECTION_TYPE_MATCH:
01815 match = xmms_collection_media_filter_match (dag, mediainfo, coll,
01816 nsid, match_table);
01817 break;
01818
01819 case XMMS_COLLECTION_TYPE_SMALLER:
01820 match = xmms_collection_media_filter_smaller (dag, mediainfo, coll,
01821 nsid, match_table);
01822 break;
01823
01824 case XMMS_COLLECTION_TYPE_GREATER:
01825 match = xmms_collection_media_filter_greater (dag, mediainfo, coll,
01826 nsid, match_table);
01827 break;
01828
01829 case XMMS_COLLECTION_TYPE_IDLIST:
01830 case XMMS_COLLECTION_TYPE_QUEUE:
01831 case XMMS_COLLECTION_TYPE_PARTYSHUFFLE:
01832
01833 val = g_hash_table_lookup (mediainfo, "id");
01834 if (val != NULL) {
01835 xmmsv_get_int (val, &id);
01836 idlist = xmmsv_coll_get_idlist (coll);
01837 for (i = 0; idlist[i] != 0; i++) {
01838
01839 if (idlist[i] == id) {
01840 match = TRUE;
01841 break;
01842 }
01843 }
01844 }
01845 break;
01846
01847
01848 default:
01849 XMMS_DBG ("invalid collection operator in xmms_collection_media_match");
01850 g_assert_not_reached ();
01851 break;
01852 }
01853
01854 return match;
01855 }
01856
01857
01858
01859
01860
01861
01862
01863
01864
01865
01866
01867
01868 static gboolean
01869 xmms_collection_media_match_reference (xmms_coll_dag_t *dag, GHashTable *mediainfo,
01870 xmmsv_coll_t *coll, guint nsid,
01871 GHashTable *match_table,
01872 const gchar *refname, const gchar *refns)
01873 {
01874 gboolean match;
01875 guint refnsid;
01876 coll_find_state_t *matchstate;
01877
01878
01879 refnsid = xmms_collection_get_namespace_id (refns);
01880 if (refnsid == nsid) {
01881 matchstate = g_hash_table_lookup (match_table, refname);
01882 if (*matchstate == XMMS_COLLECTION_FIND_STATE_UNCHECKED) {
01883
01884 matchstate = g_new (coll_find_state_t, 1);
01885 match = xmms_collection_media_match_operand (dag,
01886 mediainfo,
01887 coll, nsid,
01888 match_table);
01889
01890 if (match) {
01891 *matchstate = XMMS_COLLECTION_FIND_STATE_MATCH;
01892 } else {
01893 *matchstate = XMMS_COLLECTION_FIND_STATE_NOMATCH;
01894 }
01895
01896 g_hash_table_replace (match_table, g_strdup (refname), matchstate);
01897
01898 } else {
01899 match = (*matchstate == XMMS_COLLECTION_FIND_STATE_MATCH);
01900 }
01901
01902
01903 } else {
01904 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
01905 nsid, match_table);
01906 }
01907
01908 return match;
01909 }
01910
01911
01912
01913
01914
01915
01916
01917
01918
01919
01920
01921 static gboolean
01922 xmms_collection_media_match_operand (xmms_coll_dag_t *dag, GHashTable *mediainfo,
01923 xmmsv_coll_t *coll, guint nsid,
01924 GHashTable *match_table)
01925 {
01926 xmmsv_coll_t *op;
01927 gboolean match = FALSE;
01928
01929 xmmsv_coll_operand_list_save (coll);
01930 xmmsv_coll_operand_list_first (coll);
01931 if (xmmsv_coll_operand_list_entry (coll, &op)) {
01932 match = xmms_collection_media_match (dag, mediainfo, op, nsid, match_table);
01933 }
01934 xmmsv_coll_operand_list_restore (coll);
01935
01936 return match;
01937 }
01938
01939
01940
01941
01942
01943
01944 static GHashTable *
01945 xmms_collection_media_info (guint mid, xmms_error_t *err)
01946 {
01947 GList *res;
01948 GList *n;
01949 GHashTable *infos;
01950 gchar *name;
01951 const gchar *buf;
01952 xmmsv_t *cmdval;
01953 xmmsv_t *value;
01954 guint state;
01955
01956
01957 res = xmms_medialib_info_list (NULL, mid, err);
01958
01959
01960 infos = g_hash_table_new_full (g_str_hash, g_str_equal,
01961 g_free, (GDestroyNotify) xmmsv_unref);
01962 for (state = 0, n = res; n; state = (state + 1) % 3, n = n->next) {
01963 switch (state) {
01964 case 0:
01965 break;
01966
01967 case 1:
01968 cmdval = n->data;
01969 xmmsv_get_string (cmdval, &buf);
01970 name = g_strdup (buf);
01971 break;
01972
01973 case 2:
01974 value = xmmsv_ref (n->data);
01975
01976
01977 if (g_hash_table_lookup (infos, name) == NULL) {
01978 g_hash_table_replace (infos, name, value);
01979 }
01980 break;
01981 }
01982
01983 xmmsv_unref (n->data);
01984 }
01985
01986 g_list_free (res);
01987
01988 return infos;
01989 }
01990
01991
01992
01993
01994
01995
01996 static gboolean
01997 filter_get_mediainfo_field_string (xmmsv_coll_t *coll,
01998 GHashTable *mediainfo, gchar **val)
01999 {
02000 gboolean retval = FALSE;
02001 gchar *attr;
02002 xmmsv_t *cmdval;
02003
02004 if (xmmsv_coll_attribute_get (coll, "field", &attr)) {
02005 cmdval = g_hash_table_lookup (mediainfo, attr);
02006 if (cmdval != NULL) {
02007 switch (xmmsv_get_type (cmdval)) {
02008 case XMMSV_TYPE_STRING:
02009 {
02010 const gchar *s;
02011 xmmsv_get_string (cmdval, &s);
02012 *val = g_strdup (s);
02013 retval = TRUE;
02014 break;
02015 }
02016 case XMMSV_TYPE_INT32:
02017 {
02018 gint i;
02019 xmmsv_get_int (cmdval, &i);
02020 *val = g_strdup_printf ("%d", i);
02021 retval = TRUE;
02022 break;
02023 }
02024 default:
02025 break;
02026 }
02027 }
02028 }
02029
02030 return retval;
02031 }
02032
02033
02034
02035
02036
02037
02038 static gboolean
02039 filter_get_mediainfo_field_int (xmmsv_coll_t *coll, GHashTable *mediainfo, gint *val)
02040 {
02041 gboolean retval = FALSE;
02042 gchar *attr;
02043 xmmsv_t *cmdval;
02044
02045 if (xmmsv_coll_attribute_get (coll, "field", &attr)) {
02046 cmdval = g_hash_table_lookup (mediainfo, attr);
02047 if (cmdval != NULL && xmmsv_get_type (cmdval) == XMMSV_TYPE_INT32) {
02048 xmmsv_get_int (cmdval, val);
02049 retval = TRUE;
02050 }
02051 }
02052
02053 return retval;
02054 }
02055
02056
02057 static gboolean
02058 filter_get_operator_value_string (xmmsv_coll_t *coll, const gchar **val)
02059 {
02060 gchar *attr;
02061 gboolean valid;
02062
02063 valid = xmmsv_coll_attribute_get (coll, "value", &attr);
02064 if (valid) {
02065 *val = attr;
02066 }
02067
02068 return valid;
02069 }
02070
02071
02072 static gboolean
02073 filter_get_operator_value_int (xmmsv_coll_t *coll, gint *val)
02074 {
02075 gint buf;
02076 gboolean valid;
02077
02078 valid = xmms_collection_get_int_attr (coll, "value", &buf);
02079 if (valid) {
02080 *val = buf;
02081 }
02082
02083 return valid;
02084 }
02085
02086
02087
02088 static gboolean
02089 filter_get_operator_case (xmmsv_coll_t *coll, gboolean *val)
02090 {
02091 gchar *attr;
02092
02093 if (xmmsv_coll_attribute_get (coll, "case-sensitive", &attr)) {
02094 *val = (strcmp (attr, "true") == 0);
02095 }
02096 else {
02097 *val = FALSE;
02098 }
02099
02100 return TRUE;
02101 }
02102
02103
02104 static gboolean
02105 xmms_collection_media_filter_has (xmms_coll_dag_t *dag, GHashTable *mediainfo,
02106 xmmsv_coll_t *coll, guint nsid,
02107 GHashTable *match_table)
02108 {
02109 gboolean match = FALSE;
02110 gchar *mediaval;
02111
02112
02113 if (filter_get_mediainfo_field_string (coll, mediainfo, &mediaval)) {
02114 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
02115 nsid, match_table);
02116
02117 g_free (mediaval);
02118 }
02119
02120 return match;
02121 }
02122
02123
02124 static gboolean
02125 xmms_collection_media_filter_equals (xmms_coll_dag_t *dag, GHashTable *mediainfo,
02126 xmmsv_coll_t *coll, guint nsid,
02127 GHashTable *match_table)
02128 {
02129 gboolean match = FALSE;
02130 gchar *mediaval = NULL;
02131 const gchar *opval;
02132 gboolean case_sens;
02133
02134 if (filter_get_mediainfo_field_string (coll, mediainfo, &mediaval) &&
02135 filter_get_operator_value_string (coll, &opval) &&
02136 filter_get_operator_case (coll, &case_sens)) {
02137
02138 if (case_sens) {
02139 match = (strcmp (mediaval, opval) == 0);
02140 } else {
02141 match = (g_ascii_strcasecmp (mediaval, opval) == 0);
02142 }
02143 }
02144
02145
02146 if (match) {
02147 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
02148 nsid, match_table);
02149 }
02150
02151 if (mediaval != NULL) {
02152 g_free (mediaval);
02153 }
02154
02155 return match;
02156 }
02157
02158
02159 static gboolean
02160 xmms_collection_media_filter_match (xmms_coll_dag_t *dag, GHashTable *mediainfo,
02161 xmmsv_coll_t *coll, guint nsid,
02162 GHashTable *match_table)
02163 {
02164 gboolean match = FALSE;
02165 gchar *buf, *opval, *mediaval;
02166 const gchar *s;
02167 gboolean case_sens;
02168
02169 if (filter_get_mediainfo_field_string (coll, mediainfo, &buf) &&
02170 filter_get_operator_value_string (coll, &s) &&
02171 filter_get_operator_case (coll, &case_sens)) {
02172
02173
02174 if (case_sens) {
02175 opval = g_strdup (s);
02176 mediaval = g_strdup (buf);
02177 } else {
02178 opval = g_utf8_strdown (s, -1);
02179 mediaval = g_utf8_strdown (buf, -1);
02180 }
02181
02182 match = g_pattern_match_simple (opval, mediaval);
02183
02184 g_free (buf);
02185 g_free (opval);
02186 g_free (mediaval);
02187
02188
02189 if (match) {
02190 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
02191 nsid, match_table);
02192 }
02193 }
02194
02195 return match;
02196 }
02197
02198
02199 static gboolean
02200 xmms_collection_media_filter_smaller (xmms_coll_dag_t *dag, GHashTable *mediainfo,
02201 xmmsv_coll_t *coll, guint nsid,
02202 GHashTable *match_table)
02203 {
02204 gboolean match = FALSE;
02205 gint mediaval;
02206 gint opval;
02207
02208
02209 if (filter_get_mediainfo_field_int (coll, mediainfo, &mediaval) &&
02210 filter_get_operator_value_int (coll, &opval) &&
02211 (mediaval < opval) ) {
02212
02213 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
02214 nsid, match_table);
02215 }
02216
02217 return match;
02218 }
02219
02220
02221 static gboolean
02222 xmms_collection_media_filter_greater (xmms_coll_dag_t *dag, GHashTable *mediainfo,
02223 xmmsv_coll_t *coll, guint nsid,
02224 GHashTable *match_table)
02225 {
02226 gboolean match = FALSE;
02227 gint mediaval;
02228 gint opval;
02229
02230
02231 if (filter_get_mediainfo_field_int (coll, mediainfo, &mediaval) &&
02232 filter_get_operator_value_int (coll, &opval) &&
02233 (mediaval > opval) ) {
02234
02235 match = xmms_collection_media_match_operand (dag, mediainfo, coll,
02236 nsid, match_table);
02237 }
02238
02239 return match;
02240 }