XMMS2
collection.c
Go to the documentation of this file.
1 /* XMMS2 - X Music Multiplexer System
2  * Copyright (C) 2003-2011 XMMS2 Team
3  *
4  * PLUGINS ARE NOT CONSIDERED TO BE DERIVED WORK !!!
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14  * Lesser General Public License for more details.
15  */
16 
17 
18 /** @file
19  * Manages collections
20  */
21 
22 #include <stdio.h>
23 #include <unistd.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <glib.h>
27 #include <math.h>
28 
30 #include "xmmspriv/xmms_playlist.h"
33 #include "xmmspriv/xmms_collsync.h"
34 #include "xmmspriv/xmms_xform.h"
36 #include "xmms/xmms_ipc.h"
37 #include "xmms/xmms_config.h"
38 #include "xmms/xmms_log.h"
39 
40 
41 /* Internal helper structures */
42 
43 typedef struct {
44  const gchar *name;
45  const gchar *namespace;
46  xmmsv_coll_t *oldtarget;
47  xmmsv_coll_t *newtarget;
48 } coll_rebind_infos_t;
49 
50 typedef struct {
51  const gchar* oldname;
52  const gchar* newname;
53  const gchar* namespace;
54 } coll_rename_infos_t;
55 
56 typedef struct {
57  xmms_coll_dag_t *dag;
58  FuncApplyToColl func;
59  void *udata;
60 } coll_call_infos_t;
61 
62 typedef struct {
63  const gchar *target_name;
64  const gchar *target_namespace;
65  gboolean found;
66 } coll_refcheck_t;
67 
68 typedef struct {
69  const gchar *key;
70  xmmsv_coll_t *value;
71 } coll_table_pair_t;
72 
73 typedef enum {
78 
79 typedef struct add_metadata_from_tree_user_data_St {
80  xmms_medialib_session_t *session;
82  guint src;
84 
85 static GList *global_stream_type;
86 
87 /* Functions */
88 
89 static void xmms_collection_destroy (xmms_object_t *object);
90 
91 static gboolean xmms_collection_validate (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *save_name, const gchar *save_namespace);
92 static gboolean xmms_collection_validate_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *save_name, const gchar *save_namespace);
93 static gboolean xmms_collection_unreference (xmms_coll_dag_t *dag, const gchar *name, guint nsid);
94 
95 static gboolean xmms_collection_has_reference_to (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, const gchar *tg_name, const gchar *tg_ns);
96 
97 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);
98 
99 static void call_apply_to_coll (gpointer name, gpointer coll, gpointer udata);
100 static void prepend_key_string (gpointer key, gpointer value, gpointer udata);
101 static gboolean value_match_save_key (gpointer key, gpointer val, gpointer udata);
102 
103 static void rebind_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
104 static void rename_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
105 static void strip_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
106 static void check_for_reference (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata);
107 
108 static void coll_unref (void *coll);
109 
110 static GHashTable *xmms_collection_media_info (xmms_medialib_entry_t mid, xmms_error_t *err);
111 
112 static gboolean filter_get_mediainfo_field_string (xmmsv_coll_t *coll, GHashTable *mediainfo, gchar **val);
113 static gboolean filter_get_mediainfo_field_int (xmmsv_coll_t *coll, GHashTable *mediainfo, gint *val);
114 static gboolean filter_get_operator_value_string (xmmsv_coll_t *coll, const gchar **val);
115 static gboolean filter_get_operator_value_int (xmmsv_coll_t *coll, gint *val);
116 static gboolean filter_get_operator_case (xmmsv_coll_t *coll, gboolean *val);
117 
118 static void build_match_table (gpointer key, gpointer value, gpointer udata);
119 static gboolean find_unchecked (gpointer name, gpointer value, gpointer udata);
120 static void build_list_matches (gpointer key, gpointer value, gpointer udata);
121 
122 static gboolean xmms_collection_media_match (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
123 static gboolean xmms_collection_media_match_operand (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
124 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);
125 static gboolean xmms_collection_media_filter_has (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
126 static gboolean xmms_collection_media_filter_equals (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
127 static gboolean xmms_collection_media_filter_match (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
128 static gboolean xmms_collection_media_filter_smaller (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
129 static gboolean xmms_collection_media_filter_greater (xmms_coll_dag_t *dag, GHashTable *mediainfo, xmmsv_coll_t *coll, guint nsid, GHashTable *match_table);
130 
131 static xmmsv_coll_t * xmms_collection_client_get (xmms_coll_dag_t *dag, const gchar *collname, const gchar *namespace, xmms_error_t *error);
132 static GList * xmms_collection_client_list (xmms_coll_dag_t *dag, const gchar *namespace, xmms_error_t *error);
133 static void xmms_collection_client_save (xmms_coll_dag_t *dag, const gchar *name, const gchar *namespace, xmmsv_coll_t *coll, xmms_error_t *error);
134 static void xmms_collection_client_remove (xmms_coll_dag_t *dag, const gchar *collname, const gchar *namespace, xmms_error_t *error);
135 static GList * xmms_collection_client_find (xmms_coll_dag_t *dag, gint32 mid, const gchar *namespace, xmms_error_t *error);
136 static void xmms_collection_client_rename (xmms_coll_dag_t *dag, const gchar *from_name, const gchar *to_name, const gchar *namespace, xmms_error_t *error);
137 
138 static GList * xmms_collection_client_query_infos (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, gint32 lim_start, gint32 lim_len, xmmsv_t *order, xmmsv_t *fetch, xmmsv_t *group, xmms_error_t *err);
139 static GList * xmms_collection_client_query_ids (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, gint32 lim_start, gint32 lim_len, xmmsv_t *order, xmms_error_t *err);
140 static xmmsv_coll_t *xmms_collection_client_idlist_from_playlist (xmms_coll_dag_t *dag, const gchar *mediainfo, xmms_error_t *err);
141 static void xmms_collection_client_sync (xmms_coll_dag_t *dag, xmms_error_t *err);
142 
143 
144 #include "collection_ipc.c"
145 
146 GTree *
148  const gchar *plname, const gchar *namespace)
149 {
150  GTree *dict;
151 
152  dict = g_tree_new_full ((GCompareDataFunc) strcmp, NULL,
153  NULL, (GDestroyNotify)xmmsv_unref);
154 
155  g_tree_insert (dict, (gpointer) "type", xmmsv_new_int (type));
156  g_tree_insert (dict, (gpointer) "name", xmmsv_new_string (plname));
157  g_tree_insert (dict, (gpointer) "namespace", xmmsv_new_string (namespace));
158 
159  return dict;
160 }
161 
162 void
164 {
165  g_return_if_fail (colldag);
166  g_return_if_fail (dict);
167 
168  xmms_object_emit_f (XMMS_OBJECT (colldag),
171  dict);
172 
173  g_tree_destroy (dict);
174 }
175 
176 #define XMMS_COLLECTION_CHANGED_MSG(type, name, namespace) xmms_collection_changed_msg_send (dag, xmms_collection_changed_msg_new (type, name, namespace))
177 
178 
179 /** @defgroup Collection Collection
180  * @ingroup XMMSServer
181  * @brief This is the collection manager.
182  *
183  * The set of collections is stored as a DAG of collection operators.
184  * Each collection namespace contains a list of saved collections,
185  * with a pointer to the node in the graph.
186  * @{
187  */
188 
189 /** Collection DAG structure */
190 
191 struct xmms_coll_dag_St {
192  xmms_object_t object;
193 
194  /* Ref to the playlist object, needed to notify it when a playlist changes */
195  xmms_playlist_t *playlist;
196 
197  GHashTable *collrefs[XMMS_COLLECTION_NUM_NAMESPACES];
198 
199  GMutex *mutex;
200 
201 };
202 
203 static void
204 coll_sync_cb (xmms_object_t *object, xmmsv_t *val, gpointer udata)
205 {
207 }
208 
209 /** Initializes a new xmms_coll_dag_t.
210  *
211  * @returns The newly allocated collection DAG.
212  */
215 {
216  gint i;
217  xmms_coll_dag_t *ret;
219 
220  ret = xmms_object_new (xmms_coll_dag_t, xmms_collection_destroy);
221  ret->mutex = g_mutex_new ();
222  ret->playlist = playlist;
223 
224  xmms_coll_sync_init (ret);
225 
226  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
227  ret->collrefs[i] = g_hash_table_new_full (g_str_hash, g_str_equal,
228  g_free, coll_unref);
229  }
230 
231  xmms_collection_register_ipc_commands (XMMS_OBJECT (ret));
232 
233  /* Connection coll_sync_cb to some signals */
236  coll_sync_cb, ret);
237 
238  /* FIXME: These signals should trigger COLLECTION_CHANGED */
239  xmms_object_connect (XMMS_OBJECT (playlist),
241  coll_sync_cb, ret);
242 
243  xmms_object_connect (XMMS_OBJECT (playlist),
245  coll_sync_cb, ret);
246 
247  xmms_object_connect (XMMS_OBJECT (playlist),
249  coll_sync_cb, ret);
250 
251 
253 
256  "application/x-xmms2-playlist-entries",
258  global_stream_type = g_list_prepend (NULL, f);
259 
260  return ret;
261 }
262 
263 static void
264 add_metadata_from_tree (const gchar *key, xmmsv_t *value, gpointer user_data)
265 {
266  add_metadata_from_tree_user_data_t *ud = user_data;
267 
268  if (xmmsv_get_type (value) == XMMSV_TYPE_INT32) {
269  gint iv;
270  xmmsv_get_int (value, &iv);
271  xmms_medialib_entry_property_set_int_source (ud->session, ud->entry,
272  key,
273  iv,
274  ud->src);
275  } else if (xmmsv_get_type (value) == XMMSV_TYPE_STRING) {
276  const gchar *sv;
277  xmmsv_get_string (value, &sv);
278  xmms_medialib_entry_property_set_str_source (ud->session, ud->entry,
279  key,
280  sv,
281  ud->src);
282  }
283 }
284 
285 
286 /** Create a idlist from a playlist file
287  * @param dag The collection DAG.
288  * @param path URL to the playlist file
289  * @param err If error occurs, a message is stored in this variable.
290  * @returns A idlist
291  */
292 static xmmsv_coll_t *
293 xmms_collection_client_idlist_from_playlist (xmms_coll_dag_t *dag,
294  const gchar *path,
295  xmms_error_t *err)
296 {
297  xmms_xform_t *xform;
298  GList *lst, *n;
299  xmmsv_coll_t *coll;
300  xmms_medialib_session_t *session;
301  guint src;
302  const gchar *buf;
303 
304  /* we don't want any effects for playlist, so just report we're rehashing */
305  xform = xmms_xform_chain_setup_url (0, path, global_stream_type, TRUE);
306 
307  if (!xform) {
308  xmms_error_set (err, XMMS_ERROR_NO_SAUSAGE, "We can't handle this type of playlist or URL");
309  return NULL;
310  }
311 
312  lst = xmms_xform_browse_method (xform, "/", err);
313  if (xmms_error_iserror (err)) {
314  xmms_object_unref (xform);
315  return NULL;
316  }
317 
319  session = xmms_medialib_begin_write ();
320  src = xmms_medialib_source_to_id (session, "plugin/playlist");
321 
322  n = lst;
323  while (n) {
324  xmms_medialib_entry_t entry;
325 
326  xmmsv_t *a = n->data;
327  xmmsv_t *b;
328 
329  if (!xmmsv_dict_get (a, "realpath", &b)) {
330  xmms_log_error ("Playlist plugin did not set realpath; probably a bug in plugin");
331  xmmsv_unref (a);
332  n = g_list_delete_link (n, n);
333  continue;
334  }
335 
336  xmmsv_get_string (b, &buf);
337  entry = xmms_medialib_entry_new_encoded (session, buf, err);
338  xmmsv_dict_remove (a, "realpath");
339  xmmsv_dict_remove (a, "path");
340 
341  if (entry) {
343  udata.session = session;
344  udata.entry = entry;
345  udata.src = src;
346 
347  xmmsv_dict_foreach(a, add_metadata_from_tree, &udata);
348 
349  xmmsv_coll_idlist_append (coll, entry);
350  } else {
351  xmmsv_get_string (b, &buf);
352  xmms_log_error ("couldn't add %s to collection!", buf);
353  }
354 
355  xmmsv_unref (a);
356  n = g_list_delete_link (n, n);
357  }
358 
359  xmms_medialib_end (session);
360  xmms_object_unref (xform);
361 
362  return coll;
363 }
364 
365 /** Remove the given collection from the DAG.
366 *
367 * If to be removed from ALL namespaces, then all matching collections are removed.
368 *
369 * @param dag The collection DAG.
370 * @param name The name of the collection to remove.
371 * @param namespace The namespace where the collection to remove is (can be ALL).
372 * @param err If an error occurs, a message is stored in it.
373 * @returns True on success, false otherwise.
374 */
375 void
376 xmms_collection_client_remove (xmms_coll_dag_t *dag, const gchar *name,
377  const gchar *namespace, xmms_error_t *err)
378 {
379  guint nsid;
380  gboolean retval = FALSE;
381  guint i;
382 
383  nsid = xmms_collection_get_namespace_id (namespace);
384  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
385  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
386  return;
387  }
388 
389  g_mutex_lock (dag->mutex);
390 
391  /* Unreference the matching collection(s) */
392  if (nsid == XMMS_COLLECTION_NSID_ALL) {
393  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
394  retval = xmms_collection_unreference (dag, name, i) || retval;
395  }
396  } else {
397  retval = xmms_collection_unreference (dag, name, nsid);
398  }
399 
400  g_mutex_unlock (dag->mutex);
401 
402  if (retval == FALSE) {
403  xmms_error_set (err, XMMS_ERROR_NOENT, "Failed to remove this collection!");
404  }
405 
406 }
407 
408 /** Save the given collection in the DAG under the given name in the given namespace.
409  *
410  * @param dag The collection DAG in which to save the collection.
411  * @param name The name under which to save the collection.
412  * @param namespace The namespace in which to save th collection.
413  * @param coll The collection structure to save.
414  * @param err If an error occurs, a message is stored in it.
415  * @returns True on success, false otherwise.
416  */
417 void
418 xmms_collection_client_save (xmms_coll_dag_t *dag, const gchar *name, const gchar *namespace,
419  xmmsv_coll_t *coll, xmms_error_t *err)
420 {
421  xmmsv_coll_t *existing;
422  guint nsid;
423  const gchar *alias;
424  gchar *newkey = NULL;
425 
426  nsid = xmms_collection_get_namespace_id (namespace);
427  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
428  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
429  return;
430  } else if (nsid == XMMS_COLLECTION_NSID_ALL) {
431  xmms_error_set (err, XMMS_ERROR_GENERIC, "cannot save collection in all namespaces");
432  return;
433  }
434 
435  /* Validate collection structure */
436  if (!xmms_collection_validate (dag, coll, name, namespace)) {
437  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection structure");
438  return;
439  }
440 
441  g_mutex_lock (dag->mutex);
442 
443  /* Unreference previously saved collection */
444  existing = xmms_collection_get_pointer (dag, name, nsid);
445  if (existing != NULL) {
446  /* Rebind reference pointers to the new collection */
447  coll_rebind_infos_t infos = { name, namespace, existing, coll };
448  xmms_collection_apply_to_all_collections (dag, rebind_references, &infos);
449  }
450 
451  /* Link references in newly saved collection to actual operators */
453 
454  /* Update existing collection in the table */
455  if (existing != NULL) {
456  while ((alias = xmms_collection_find_alias (dag, nsid,
457  existing, NULL)) != NULL) {
458  newkey = g_strdup (alias);
459 
460  /* update all pairs pointing to the old coll */
461  xmms_collection_dag_replace (dag, nsid, newkey, coll);
462  xmmsv_coll_ref (coll);
463 
465  newkey,
466  namespace);
467  }
468 
469  /* Save new collection in the table */
470  } else {
471  newkey = g_strdup (name);
472  xmms_collection_dag_replace (dag, nsid, newkey, coll);
473  xmmsv_coll_ref (coll);
474 
476  newkey,
477  namespace);
478  }
479 
480  g_mutex_unlock (dag->mutex);
481 
482  /* If updating a playlist, trigger PLAYLIST_CHANGED */
483  if (nsid == XMMS_COLLECTION_NSID_PLAYLISTS) {
484  XMMS_PLAYLIST_COLLECTION_CHANGED_MSG (dag->playlist, newkey);
485  }
486 
487 }
488 
489 
490 /** Retrieve the structure of a given collection.
491  *
492  * If looking in ALL namespaces, only the collection first found is returned!
493  *
494  * @param dag The collection DAG.
495  * @param name The name of the collection to retrieve.
496  * @param namespace The namespace in which to look for the collection.
497  * @param err If an error occurs, a message is stored in it.
498  * @returns The collection structure if found, NULL otherwise.
499  */
500 xmmsv_coll_t *
501 xmms_collection_client_get (xmms_coll_dag_t *dag, const gchar *name,
502  const gchar *namespace, xmms_error_t *err)
503 {
504  xmmsv_coll_t *coll = NULL;
505  guint nsid;
506 
507  nsid = xmms_collection_get_namespace_id (namespace);
508  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
509  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
510  return NULL;
511  }
512 
513  g_mutex_lock (dag->mutex);
514 
515  coll = xmms_collection_get_pointer (dag, name, nsid);
516 
517  /* Not found! */
518  if (coll == NULL) {
519  xmms_error_set (err, XMMS_ERROR_NOENT, "no such collection");
520 
521  /* New reference, will be freed after being put in the return message */
522  } else {
523  xmmsv_coll_ref (coll);
524  }
525 
526  g_mutex_unlock (dag->mutex);
527 
528  return coll;
529 }
530 
531 
532 /** Synchronize collection data to the database (i.e. to disk).
533  *
534  * @param dag The collection DAG.
535  * @param err If an error occurs, a message is stored in it.
536  */
537 
538 void
540 {
541  g_return_if_fail (dag);
542 
543  g_mutex_lock (dag->mutex);
544 
546 
547  g_mutex_unlock (dag->mutex);
548 }
549 
550 
551 void
552 xmms_collection_client_sync (xmms_coll_dag_t *dag, xmms_error_t *err)
553 {
554  xmms_collection_sync (dag);
555 }
556 
557 
558 /** Lists the collections in the given namespace.
559  *
560  * @param dag The collection DAG.
561  * @param namespace The namespace to list collections from (can be ALL).
562  * @param err If an error occurs, a message is stored in it.
563  * @returns A newly allocated GList with the list of collection names.
564  * Remember that it is only the LIST that is copied. Not the entries.
565  * The entries are however referenced, and must be unreffed!
566  */
567 GList *
568 xmms_collection_client_list (xmms_coll_dag_t *dag, const gchar *namespace,
569  xmms_error_t *err)
570 {
571  GList *r = NULL;
572  guint nsid;
573 
574  nsid = xmms_collection_get_namespace_id (namespace);
575  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
576  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
577  return NULL;
578  }
579 
580  g_mutex_lock (dag->mutex);
581 
582  /* Get the list of collections in the given namespace */
583  xmms_collection_foreach_in_namespace (dag, nsid, prepend_key_string, &r);
584 
585  g_mutex_unlock (dag->mutex);
586 
587  return r;
588 }
589 
590 
591 /** Find all collections in the given namespace that contain a given media.
592  *
593  * @param dag The collection DAG.
594  * @param mid The id of the media.
595  * @param namespace The namespace in which to look for collections.
596  * @param err If an error occurs, a message is stored in it.
597  * @returns A newly allocated GList with the names of the matching collections.
598  */
599 GList *
600 xmms_collection_client_find (xmms_coll_dag_t *dag, gint32 mid, const gchar *namespace,
601  xmms_error_t *err)
602 {
603  GHashTable *mediainfo;
604  GList *ret = NULL;
605  guint nsid;
606  gchar *open_name;
607  GHashTable *match_table;
608  xmmsv_coll_t *coll;
609 
610  /* Verify namespace */
611  nsid = xmms_collection_get_namespace_id (namespace);
612  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
613  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
614  return NULL;
615  }
616  if (nsid == XMMS_COLLECTION_NSID_ALL) {
617  xmms_error_set (err, XMMS_ERROR_INVAL, "cannot search in all namespaces");
618  return NULL;
619  }
620 
621  /* Prepare the match table of all collections for the given namespace */
622  match_table = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free);
623  xmms_collection_foreach_in_namespace (dag, nsid, build_match_table, match_table);
624 
625  /* Get all infos for the given mid */
626  mediainfo = xmms_collection_media_info (mid, err);
627 
628  /* While not all collections have been checked, check next */
629  while (g_hash_table_find (match_table, find_unchecked, &open_name) != NULL) {
630  coll_find_state_t *match = g_new (coll_find_state_t, 1);
631  coll = xmms_collection_get_pointer (dag, open_name, nsid);
632  if (xmms_collection_media_match (dag, mediainfo, coll, nsid, match_table)) {
634  } else {
636  }
637  g_hash_table_replace (match_table, g_strdup (open_name), match);
638  }
639 
640  /* List matching collections */
641  g_hash_table_foreach (match_table, build_list_matches, &ret);
642  g_hash_table_destroy (match_table);
643 
644  g_hash_table_destroy (mediainfo);
645 
646  return ret;
647 }
648 
649 
650 /** Rename a collection in a given namespace.
651  *
652  * @param dag The collection DAG.
653  * @param from_name The name of the collection to rename.
654  * @param to_name The new name of the collection.
655  * @param namespace The namespace to consider (cannot be ALL).
656  * @param err If an error occurs, a message is stored in it.
657  * @return True if a collection was found and renamed.
658  */
659 void
660 xmms_collection_client_rename (xmms_coll_dag_t *dag, const gchar *from_name,
661  const gchar *to_name, const gchar *namespace,
662  xmms_error_t *err)
663 {
664  gboolean retval;
665  guint nsid;
666  xmmsv_coll_t *from_coll, *to_coll;
667 
668  nsid = xmms_collection_get_namespace_id (namespace);
669  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
670  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection namespace");
671  return;
672  } else if (nsid == XMMS_COLLECTION_NSID_ALL) {
673  xmms_error_set (err, XMMS_ERROR_GENERIC, "cannot rename collection in all namespaces");
674  return;
675  }
676 
677  g_mutex_lock (dag->mutex);
678 
679  from_coll = xmms_collection_get_pointer (dag, from_name, nsid);
680  to_coll = xmms_collection_get_pointer (dag, to_name, nsid);
681 
682  /* Input validation */
683  if (from_coll == NULL) {
684  xmms_error_set (err, XMMS_ERROR_NOENT, "no such collection");
685  retval = FALSE;
686 
687  } else if (to_coll != NULL) {
688  xmms_error_set (err, XMMS_ERROR_NOENT, "a collection already exists with the target name");
689  retval = FALSE;
690 
691  /* Update collection name everywhere */
692  } else {
693  GTree *dict;
694 
695  /* insert new pair in hashtable */
696  xmms_collection_dag_replace (dag, nsid, g_strdup (to_name), from_coll);
697  xmmsv_coll_ref (from_coll);
698 
699  /* remove old pair from hashtable */
700  g_hash_table_remove (dag->collrefs[nsid], from_name);
701 
702  /* update name in all reference operators */
703  coll_rename_infos_t infos = { from_name, to_name, namespace };
704  xmms_collection_apply_to_all_collections (dag, rename_references, &infos);
705 
706  /* Send _RENAME signal */
708  from_name, namespace);
709  g_tree_insert (dict, (gpointer) "newname", xmmsv_new_string (to_name));
711 
712  retval = TRUE;
713  }
714 
715  g_mutex_unlock (dag->mutex);
716 
717 }
718 
719 
720 /** Find the ids of the media matched by a collection.
721  *
722  * @param dag The collection DAG.
723  * @param coll The collection used to match media.
724  * @param lim_start The beginning index of the LIMIT statement (0 to disable).
725  * @param lim_len The number of entries of the LIMIT statement (0 to disable).
726  * @param order The list of properties to order by (empty to disable).
727  * @param err If an error occurs, a message is stored in it.
728  * @return A list of media ids.
729  */
730 GList *
732  gint32 lim_start, gint32 lim_len, xmmsv_t *order,
733  xmms_error_t *err)
734 {
735  GList *res, *n;
736  xmmsv_t *fetch, *group, *idval;
737 
738  /* no grouping, fetch only id */
739  group = xmmsv_new_list ();
740  fetch = xmmsv_new_list ();
741  idval = xmmsv_new_string ("id");
742  xmmsv_list_append (fetch, idval);
743 
744  res = xmms_collection_client_query_infos (dag, coll, lim_start, lim_len, order, fetch, group, err);
745 
746  /* FIXME: get an uint list directly ! (we're getting ints here actually) */
747  for (n = res; n; n = n->next) {
749  xmmsv_t *id_val, *cmdval = n->data;
750 
751  xmmsv_dict_get (cmdval, "id", &id_val);
752  xmmsv_get_int (id_val, &id);
753  n->data = xmmsv_new_int (id);
754 
755  xmmsv_unref (cmdval);
756  }
757 
758  xmmsv_unref (group);
759  xmmsv_unref (fetch);
760  xmmsv_unref (idval);
761 
762  return res;
763 }
764 
765 
766 GList *
767 xmms_collection_client_query_ids (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
768  gint32 lim_start, gint32 lim_len, xmmsv_t *order,
769  xmms_error_t *err)
770 {
771  return xmms_collection_query_ids (dag, coll, lim_start, lim_len, order, err);
772 }
773 /** Find the properties of the media matched by a collection.
774  *
775  * @param dag The collection DAG.
776  * @param coll The collection used to match media.
777  * @param lim_start The beginning index of the LIMIT statement (0 to disable).
778  * @param lim_len The number of entries of the LIMIT statement (0 to disable).
779  * @param order The list of properties to order by, prefix by '-' to invert (empty to disable).
780  * @param fetch The list of properties to be retrieved.
781  * @param group The list of properties to group by (empty to disable).
782  * @param err If an error occurs, a message is stored in it.
783  * @return A list of property dicts for each entry.
784  */
785 GList *
786 xmms_collection_client_query_infos (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
787  gint32 lim_start, gint32 lim_len, xmmsv_t *order,
788  xmmsv_t *fetch, xmmsv_t *group, xmms_error_t *err)
789 {
790  GList *res = NULL;
791  GString *query;
792 
793  /* check that fetch is not empty */
794  if (xmmsv_list_get_size (fetch) == 0) {
795  xmms_error_set (err, XMMS_ERROR_INVAL, "fetch list must not be empty!");
796  return NULL;
797  }
798 
799  /* check for invalid property strings */
800  if (!check_string_list (order)) {
801  xmms_error_set (err, XMMS_ERROR_NOENT, "invalid order list!");
802  return NULL;
803  }
804  if (!check_string_list (fetch)) {
805  xmms_error_set (err, XMMS_ERROR_NOENT, "invalid fetch list!");
806  return NULL;
807  }
808  if (!check_string_list (group)) {
809  xmms_error_set (err, XMMS_ERROR_NOENT, "invalid group list!");
810  return NULL;
811  }
812 
813  /* validate the collection to query */
814  if (!xmms_collection_validate (dag, coll, NULL, NULL)) {
815  if (err) {
816  xmms_error_set (err, XMMS_ERROR_INVAL, "invalid collection structure");
817  }
818  return NULL;
819  }
820 
821  g_mutex_lock (dag->mutex);
822 
823  query = xmms_collection_get_query (dag, coll, lim_start, lim_len,
824  order, fetch, group);
825 
826  g_mutex_unlock (dag->mutex);
827 
828  XMMS_DBG ("COLLECTIONS: query_infos with %s", query->str);
829 
830  /* Run the query */
832  res = xmms_medialib_select (session, query->str, err);
833  xmms_medialib_end (session);
834 
835  g_string_free (query, TRUE);
836 
837  return res;
838 }
839 
840 /**
841  * Update a reference to point to a new collection.
842  *
843  * @param dag The collection DAG.
844  * @param name The name of the reference to update.
845  * @param nsid The namespace in which to locate the reference.
846  * @param newtarget The new collection pointed to by the reference.
847  */
848 void
850  guint nsid, xmmsv_coll_t *newtarget)
851 {
852  xmms_collection_dag_replace (dag, nsid, g_strdup (name), newtarget);
853  xmmsv_coll_ref (newtarget);
854 }
855 
856 /** Update the DAG to update the value of the pair with the given key. */
857 void
860  gchar *key, xmmsv_coll_t *newcoll)
861 {
862  g_hash_table_replace (dag->collrefs[nsid], key, newcoll);
863 }
864 
865 /** Find the collection structure corresponding to the given name in the given namespace.
866  *
867  * @param dag The collection DAG.
868  * @param collname The name of the collection to find.
869  * @param nsid The namespace id.
870  * @returns The collection structure if found, NULL otherwise.
871  */
872 xmmsv_coll_t *
873 xmms_collection_get_pointer (xmms_coll_dag_t *dag, const gchar *collname,
874  guint nsid)
875 {
876  gint i;
877  xmmsv_coll_t *coll = NULL;
878 
879  if (nsid == XMMS_COLLECTION_NSID_ALL) {
880  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES && coll == NULL; ++i) {
881  coll = g_hash_table_lookup (dag->collrefs[i], collname);
882  }
883  } else {
884  coll = g_hash_table_lookup (dag->collrefs[nsid], collname);
885  }
886 
887  return coll;
888 }
889 
890 /** Extract an attribute from a collection as an integer.
891  *
892  * @param coll The collection to extract the attribute from.
893  * @param attrname The name of the attribute.
894  * @param val The integer value of the attribute will be saved in this pointer.
895  * @return TRUE if attribute correctly read, FALSE otherwise
896  */
897 gboolean
898 xmms_collection_get_int_attr (xmmsv_coll_t *coll, const gchar *attrname, gint *val)
899 {
900  gboolean retval = FALSE;
901  gint buf;
902  gchar *str;
903  gchar *endptr;
904 
905  if (xmmsv_coll_attribute_get (coll, attrname, &str)) {
906  buf = strtol (str, &endptr, 10);
907 
908  /* Valid integer string */
909  if (*endptr == '\0') {
910  *val = buf;
911  retval = TRUE;
912  }
913  }
914 
915  return retval;
916 }
917 
918 /** Set the attribute of a collection as an integer.
919  *
920  * @param coll The collection in which to set the attribute.
921  * @param attrname The name of the attribute.
922  * @param newval The new value of the attribute.
923  * @return TRUE if attribute successfully saved, FALSE otherwise.
924  */
925 gboolean
926 xmms_collection_set_int_attr (xmmsv_coll_t *coll, const gchar *attrname,
927  gint newval)
928 {
929  gboolean retval = FALSE;
930  gchar str[XMMS_MAX_INT_ATTRIBUTE_LEN + 1];
931  gint written;
932 
933  written = g_snprintf (str, sizeof (str), "%d", newval);
934  if (written < XMMS_MAX_INT_ATTRIBUTE_LEN) {
935  xmmsv_coll_attribute_set (coll, attrname, str);
936  retval = TRUE;
937  }
938 
939  return retval;
940 }
941 
942 
943 /**
944  * Reverse-search the list of collections in the given namespace to
945  * find the first pair whose value matches the argument. If key is
946  * not NULL, any pair with the same key will be ignored.
947  *
948  * @param dag The collection DAG.
949  * @param nsid The id of the namespace to consider.
950  * @param value The value of the pair to find.
951  * @param key If not NULL, ignore any pair with that key.
952  * @return The key of the found pair.
953  */
954 const gchar *
956  xmmsv_coll_t *value, const gchar *key)
957 {
958  const gchar *otherkey = NULL;
959  coll_table_pair_t search_pair = { key, value };
960 
961  if (g_hash_table_find (dag->collrefs[nsid], value_match_save_key,
962  &search_pair) != NULL) {
963  otherkey = search_pair.key;
964  }
965 
966  return otherkey;
967 }
968 
969 
970 /**
971  * Get a random media entry from the given collection.
972  *
973  * @param dag The collection DAG.
974  * @param source The collection to query.
975  * @return A random media from the source collection, or 0 if none found.
976  */
979 {
980  GList *res;
981  xmms_medialib_entry_t mid = 0;
982  xmmsv_t *rorder = xmmsv_new_list ();
983  xmmsv_t *randval = xmmsv_new_string ("~RANDOM()");
984 
985  /* FIXME: Temporary hack to allow custom ordering functions */
986  xmmsv_list_append (rorder, randval);
987 
988  res = xmms_collection_query_ids (dag, source, 0, 1, rorder, NULL);
989 
990  if (res != NULL) {
991  xmmsv_t *val = (xmmsv_t *) res->data;
992  xmmsv_get_int (val, &mid);
993  xmmsv_unref (val);
994  g_list_free (res);
995  }
996 
997  xmmsv_unref (rorder);
998  xmmsv_unref (randval);
999 
1000  return mid;
1001 }
1002 
1003 /** @} */
1004 
1005 
1006 
1007 /** Free the collection DAG and other memory in the xmms_coll_dag_t
1008  *
1009  * This will free all collections in the DAG!
1010  */
1011 static void
1012 xmms_collection_destroy (xmms_object_t *object)
1013 {
1014  gint i;
1015  xmms_coll_dag_t *dag = (xmms_coll_dag_t *)object;
1016 
1017  g_return_if_fail (dag);
1018 
1021 
1022  g_mutex_free (dag->mutex);
1023 
1024  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
1025  g_hash_table_destroy (dag->collrefs[i]); /* dag is freed here */
1026  }
1027 
1028  xmms_collection_unregister_ipc_commands ();
1029 }
1030 
1031 /** Validate the given collection against a DAG.
1032  *
1033  * @param dag The collection DAG.
1034  * @param coll The collection to validate.
1035  * @param save_name The name under which the collection will be saved (NULL
1036  * if none).
1037  * @param save_namespace The namespace in which the collection will be
1038  * saved (NULL if none).
1039  * @returns True if the collection is valid, false otherwise.
1040  */
1041 static gboolean
1042 xmms_collection_validate (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
1043  const gchar *save_name, const gchar *save_namespace)
1044 {
1045  /* Special validation checks for the Playlists namespace */
1046  if (save_namespace != NULL &&
1047  strcmp (save_namespace, XMMS_COLLECTION_NS_PLAYLISTS) == 0) {
1048  /* only accept idlists */
1052  return FALSE;
1053  }
1054  }
1055 
1056  /* Standard checking of the whole coll DAG */
1057  return xmms_collection_validate_recurs (dag, coll, save_name,
1058  save_namespace);
1059 }
1060 
1061 /**
1062  * Internal recursive validation function used to validate the whole
1063  * graph of a collection.
1064  */
1065 static gboolean
1066 xmms_collection_validate_recurs (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
1067  const gchar *save_name, const gchar *save_namespace)
1068 {
1069  guint num_operands = 0;
1070  xmmsv_coll_t *op, *ref;
1071  gchar *attr, *attr2;
1072  gboolean valid = TRUE;
1073  xmmsv_coll_type_t type;
1075 
1076  /* count operands */
1077  num_operands = xmmsv_list_get_size (xmmsv_coll_operands_get (coll));
1078 
1079  /* analyse by type */
1080  type = xmmsv_coll_get_type (coll);
1081  switch (type) {
1083  /* zero or one (bound in DAG) operand */
1084  if (num_operands > 1) {
1085  return FALSE;
1086  }
1087 
1088  /* check if referenced collection exists */
1089  xmmsv_coll_attribute_get (coll, "reference", &attr);
1090  if (attr == NULL) {
1091  return FALSE;
1092  } else if (strcmp (attr, "All Media") != 0) {
1093  xmmsv_coll_attribute_get (coll, "namespace", &attr2);
1094 
1095  if (attr2 == NULL) {
1096  return FALSE;
1097  }
1098 
1099  nsid = xmms_collection_get_namespace_id (attr2);
1100  if (nsid == XMMS_COLLECTION_NSID_INVALID) {
1101  return FALSE;
1102  }
1103 
1104  g_mutex_lock (dag->mutex);
1105  ref = xmms_collection_get_pointer (dag, attr, nsid);
1106  if (ref == NULL) {
1107  g_mutex_unlock (dag->mutex);
1108  return FALSE;
1109  }
1110 
1111  if (save_name && save_namespace) {
1112  /* self-reference is of course forbidden */
1113  if (strcmp (attr, save_name) == 0 &&
1114  strcmp (attr2, save_namespace) == 0) {
1115 
1116  g_mutex_unlock (dag->mutex);
1117  return FALSE;
1118 
1119  /* check if the referenced coll references this one (loop!) */
1120  } else if (xmms_collection_has_reference_to (dag, ref, save_name,
1121  save_namespace)) {
1122  g_mutex_unlock (dag->mutex);
1123  return FALSE;
1124  }
1125  }
1126 
1127  g_mutex_unlock (dag->mutex);
1128  } else {
1129  /* "All Media" reference, so no referenced coll pointer */
1130  ref = NULL;
1131  }
1132 
1133  /* ensure that the operand is consistent with the reference infos */
1134  if (num_operands == 1) {
1135  xmmsv_t *val;
1136  xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &val);
1137  xmmsv_get_coll (val, &op);
1138 
1139  if (op != ref) {
1140  return FALSE;
1141  }
1142  }
1143  break;
1144 
1147  /* need operand(s) */
1148  if (num_operands == 0) {
1149  return FALSE;
1150  }
1151  break;
1152 
1154  /* one operand */
1155  if (num_operands != 1) {
1156  return FALSE;
1157  }
1158  break;
1159 
1161  /* one operand */
1162  if (num_operands != 1) {
1163  return FALSE;
1164  }
1165 
1166  /* "field" attribute */
1167  /* with valid value */
1168  if (!xmmsv_coll_attribute_get (coll, "field", &attr)) {
1169  return FALSE;
1170  }
1171  break;
1172 
1177  /* one operand */
1178  if (num_operands != 1) {
1179  return FALSE;
1180  }
1181 
1182  /* "field"/"value" attributes */
1183  /* with valid values */
1184  if (!xmmsv_coll_attribute_get (coll, "field", &attr)) {
1185  return FALSE;
1186  }
1187  /* FIXME: valid fields?
1188  else if (...) {
1189  return FALSE;
1190  }
1191  */
1192 
1193  if (!xmmsv_coll_attribute_get (coll, "value", &attr)) {
1194  return FALSE;
1195  }
1196  break;
1197 
1200  /* no operand */
1201  if (num_operands > 0) {
1202  return FALSE;
1203  }
1204  break;
1205 
1207  /* one operand */
1208  if (num_operands != 1) {
1209  return FALSE;
1210  }
1211  break;
1212 
1213  /* invalid type */
1214  default:
1215  return FALSE;
1216  break;
1217  }
1218 
1219 
1220  /* recurse in operands */
1221  if (num_operands > 0 && type != XMMS_COLLECTION_TYPE_REFERENCE) {
1222  xmmsv_list_iter_t *iter;
1224 
1225  for (xmmsv_list_iter_first (iter);
1226  valid && xmmsv_list_iter_valid (iter);
1227  xmmsv_list_iter_next (iter)) {
1228 
1229  xmmsv_t *val;
1230  xmmsv_list_iter_entry (iter, &val);
1231  xmmsv_get_coll (val, &op);
1232 
1233  if (!xmms_collection_validate_recurs (dag, op, save_name,
1234  save_namespace)) {
1235  valid = FALSE;
1236  }
1237  }
1238 
1240  }
1241 
1242  return valid;
1243 }
1244 
1245 /** Try to unreference a collection from a given namespace.
1246  *
1247  * @param dag The collection DAG.
1248  * @param name The name of the collection to remove.
1249  * @param nsid The namespace in which to look for the collection (yes, redundant).
1250  * @returns TRUE if a collection was removed, FALSE otherwise.
1251  */
1252 static gboolean
1253 xmms_collection_unreference (xmms_coll_dag_t *dag, const gchar *name, guint nsid)
1254 {
1255  xmmsv_coll_t *existing, *active_pl;
1256  gboolean retval = FALSE;
1257 
1258  existing = g_hash_table_lookup (dag->collrefs[nsid], name);
1259  active_pl = g_hash_table_lookup (dag->collrefs[XMMS_COLLECTION_NSID_PLAYLISTS],
1261 
1262  /* Unref if collection exists, and is not pointed at by _active playlist */
1263  if (existing != NULL && existing != active_pl) {
1264  const gchar *matchkey;
1265  const gchar *nsname = xmms_collection_get_namespace_string (nsid);
1266  coll_rebind_infos_t infos = { name, nsname, existing, NULL };
1267 
1268  /* FIXME: if reference pointed to by a label, we should update
1269  * the label to point to the ref'd operator instead ! */
1270 
1271  /* Strip all references to the deleted coll, bind operator directly */
1272  xmms_collection_apply_to_all_collections (dag, strip_references, &infos);
1273 
1274  /* Remove all pairs pointing to that collection */
1275  while ((matchkey = xmms_collection_find_alias (dag, nsid,
1276  existing, NULL)) != NULL) {
1277 
1279  matchkey,
1280  nsname);
1281 
1282  g_hash_table_remove (dag->collrefs[nsid], matchkey);
1283  }
1284 
1285  retval = TRUE;
1286  }
1287 
1288  return retval;
1289 }
1290 
1291 /** Find the namespace id corresponding to a namespace string.
1292  *
1293  * @param namespace The namespace string.
1294  * @returns The namespace id.
1295  */
1297 xmms_collection_get_namespace_id (const gchar *namespace)
1298 {
1299  guint nsid;
1300 
1301  if (strcmp (namespace, XMMS_COLLECTION_NS_ALL) == 0) {
1302  nsid = XMMS_COLLECTION_NSID_ALL;
1303  } else if (strcmp (namespace, XMMS_COLLECTION_NS_COLLECTIONS) == 0) {
1305  } else if (strcmp (namespace, XMMS_COLLECTION_NS_PLAYLISTS) == 0) {
1307  } else {
1309  }
1310 
1311  return nsid;
1312 }
1313 
1314 /** Find the namespace name (string) corresponding to a namespace id.
1315  *
1316  * @param nsid The namespace id.
1317  * @returns The namespace name (string).
1318  */
1319 const gchar *
1321 {
1322  const gchar *name;
1323 
1324  switch (nsid) {
1326  name = XMMS_COLLECTION_NS_ALL;
1327  break;
1330  break;
1333  break;
1334 
1336  default:
1337  name = NULL;
1338  break;
1339  }
1340 
1341  return name;
1342 }
1343 
1344 
1345 /** Check whether a collection structure contains a reference to a given collection.
1346  *
1347  * @param dag The collection DAG.
1348  * @param coll The collection to inspect for reference.
1349  * @param tg_name The name of the collection to find a reference to.
1350  * @param tg_ns The namespace of the collection to find a reference to.
1351  * @returns True if the collection contains a reference to the given
1352  * collection, false otherwise
1353  */
1354 static gboolean
1355 xmms_collection_has_reference_to (xmms_coll_dag_t *dag, xmmsv_coll_t *coll,
1356  const gchar *tg_name, const gchar *tg_ns)
1357 {
1358  coll_refcheck_t check = { tg_name, tg_ns, FALSE };
1359  xmms_collection_apply_to_collection (dag, coll, check_for_reference, &check);
1360 
1361  return check.found;
1362 }
1363 
1364 
1365 /** Apply a function to all the collections in a given namespace.
1366  *
1367  * @param dag The collection DAG.
1368  * @param nsid The namespace id.
1369  * @param f The function to apply to all the collections.
1370  * @param udata Additional user data parameter passed to the function.
1371  */
1372 void
1373 xmms_collection_foreach_in_namespace (xmms_coll_dag_t *dag, guint nsid, GHFunc f, void *udata)
1374 {
1375  gint i;
1376 
1377  if (nsid == XMMS_COLLECTION_NSID_ALL) {
1378  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
1379  g_hash_table_foreach (dag->collrefs[i], f, udata);
1380  }
1381  } else if (nsid != XMMS_COLLECTION_NSID_INVALID) {
1382  g_hash_table_foreach (dag->collrefs[nsid], f, udata);
1383  }
1384 }
1385 
1386 /** Apply a function of type #FuncApplyToColl to all the collections in all namespaces.
1387  *
1388  * @param dag The collection DAG.
1389  * @param f The function to apply to all the collections.
1390  * @param udata Additional user data parameter passed to the function.
1391  */
1392 void
1394  FuncApplyToColl f, void *udata)
1395 {
1396  gint i;
1397  coll_call_infos_t callinfos = { dag, f, udata };
1398 
1399  for (i = 0; i < XMMS_COLLECTION_NUM_NAMESPACES; ++i) {
1400  g_hash_table_foreach (dag->collrefs[i], call_apply_to_coll, &callinfos);
1401  }
1402 }
1403 
1404 /** Apply a function of type #FuncApplyToColl to the given collection.
1405  *
1406  * @param dag The collection DAG.
1407  * @param coll The collection on which to apply the function.
1408  * @param f The function to apply to all the collections.
1409  * @param udata Additional user data parameter passed to the function.
1410  */
1411 void
1413  xmmsv_coll_t *coll,
1414  FuncApplyToColl f, void *udata)
1415 {
1416  xmms_collection_apply_to_collection_recurs (dag, coll, NULL, f, udata);
1417 }
1418 
1419 /* Internal function used for recursion (parent param, NULL by default) */
1420 static void
1421 xmms_collection_apply_to_collection_recurs (xmms_coll_dag_t *dag,
1422  xmmsv_coll_t *coll,
1423  xmmsv_coll_t *parent,
1424  FuncApplyToColl f, void *udata)
1425 {
1426  xmmsv_coll_t *op;
1427 
1428  /* Apply the function to the operator. */
1429  f (dag, coll, parent, udata);
1430 
1431  /* Recurse into the operands (if not a reference) */
1433  xmmsv_list_iter_t *iter;
1435 
1436  for (xmmsv_list_iter_first (iter);
1437  xmmsv_list_iter_valid (iter);
1438  xmmsv_list_iter_next (iter)) {
1439 
1440  xmmsv_t *val;
1441  xmmsv_list_iter_entry (iter, &val);
1442 
1443  xmmsv_get_coll (val, &op);
1444 
1445  xmms_collection_apply_to_collection_recurs (dag, op, coll, f,
1446  udata);
1447  }
1448 
1450  }
1451 }
1452 
1453 
1454 /**
1455  * Work-around function to call a function on the value of the pair.
1456  */
1457 static void
1458 call_apply_to_coll (gpointer name, gpointer coll, gpointer udata)
1459 {
1460  coll_call_infos_t *callinfos = (coll_call_infos_t*)udata;
1461 
1462  xmms_collection_apply_to_collection (callinfos->dag, coll,
1463  callinfos->func, callinfos->udata);
1464 }
1465 
1466 /**
1467  * Prepend the key string (name) to the udata list.
1468  */
1469 static void
1470 prepend_key_string (gpointer key, gpointer value, gpointer udata)
1471 {
1472  GList **list = (GList**)udata;
1473  *list = g_list_prepend (*list, xmmsv_new_string (key));
1474 }
1475 
1476 /**
1477  * Returns TRUE if the value of the pair is equal to the value stored
1478  * in the udata structure, and save the corresponding key in that
1479  * structure.
1480  */
1481 static gboolean
1482 value_match_save_key (gpointer key, gpointer val, gpointer udata)
1483 {
1484  gboolean found = FALSE;
1485  coll_table_pair_t *pair = (coll_table_pair_t*)udata;
1486  xmmsv_coll_t *coll = (xmmsv_coll_t*)val;
1487 
1488  /* value matching and key not ignored, found! */
1489  if ((coll == pair->value) &&
1490  (pair->key == NULL || strcmp (pair->key, key) != 0)) {
1491  pair->key = key;
1492  found = TRUE;
1493  }
1494 
1495  return found;
1496 }
1497 
1498 /**
1499  * If a reference, add the operator of the pointed collection as an
1500  * operand.
1501  */
1502 void
1504 {
1506  xmmsv_coll_t *target;
1507  gchar *target_name;
1508  gchar *target_namespace;
1509  gint target_nsid;
1510 
1511  xmmsv_coll_attribute_get (coll, "reference", &target_name);
1512  xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1513  if (target_name == NULL || target_namespace == NULL ||
1514  strcmp (target_name, "All Media") == 0) {
1515  return;
1516  }
1517 
1518  target_nsid = xmms_collection_get_namespace_id (target_namespace);
1519  if (target_nsid == XMMS_COLLECTION_NSID_INVALID) {
1520  return;
1521  }
1522 
1523  target = xmms_collection_get_pointer (dag, target_name, target_nsid);
1524  if (target == NULL) {
1525  return;
1526  }
1527 
1528  xmmsv_coll_add_operand (coll, target);
1529  }
1530 }
1531 
1532 /**
1533  * If a reference, rebind the given operator to the new operator
1534  * representing the referenced collection (pointers and so are in the
1535  * udata structure).
1536  */
1537 static void
1538 rebind_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1539 {
1541  coll_rebind_infos_t *infos;
1542 
1543  gchar *target_name = NULL;
1544  gchar *target_namespace = NULL;
1545 
1546  infos = (coll_rebind_infos_t*)udata;
1547 
1548  /* FIXME: Or only compare operand vs oldtarget ? */
1549 
1550  xmmsv_coll_attribute_get (coll, "reference", &target_name);
1551  xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1552  if (strcmp (infos->name, target_name) != 0 ||
1553  strcmp (infos->namespace, target_namespace) != 0) {
1554  return;
1555  }
1556 
1557  xmmsv_coll_remove_operand (coll, infos->oldtarget);
1558  xmmsv_coll_add_operand (coll, infos->newtarget);
1559  }
1560 }
1561 
1562 /**
1563  * If a reference with matching name, rename it according to the
1564  * rename infos in the udata structure.
1565  */
1566 static void
1567 rename_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1568 {
1570  coll_rename_infos_t *infos;
1571 
1572  gchar *target_name = NULL;
1573  gchar *target_namespace = NULL;
1574 
1575  infos = (coll_rename_infos_t*)udata;
1576 
1577  xmmsv_coll_attribute_get (coll, "reference", &target_name);
1578  xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1579  if (strcmp (infos->oldname, target_name) == 0 &&
1580  strcmp (infos->namespace, target_namespace) == 0) {
1581  xmmsv_coll_attribute_set (coll, "reference", infos->newname);
1582  }
1583  }
1584 }
1585 
1586 /**
1587  * Strip reference operators to the given collection by rebinding the
1588  * parent directly to the pointed operator.
1589  */
1590 static void
1591 strip_references (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1592 {
1593  xmmsv_coll_t *op;
1594  coll_rebind_infos_t *infos;
1595  gchar *target_name = NULL;
1596  gchar *target_namespace = NULL;
1597  xmmsv_list_iter_t *iter;
1598  xmmsv_t *tmp;
1599 
1600  infos = (coll_rebind_infos_t*)udata;
1601 
1603  for (xmmsv_list_iter_first (iter);
1604  xmmsv_list_iter_valid (iter);
1605  xmmsv_list_iter_next (iter)) {
1606 
1607  xmmsv_list_iter_entry (iter, &tmp);
1608  xmmsv_get_coll (tmp, &op);
1609 
1610  /* Skip if not potential reference */
1612  continue;
1613  }
1614 
1615  xmmsv_coll_attribute_get (op, "reference", &target_name);
1616  xmmsv_coll_attribute_get (op, "namespace", &target_namespace);
1617  if (strcmp (infos->name, target_name) != 0 ||
1618  strcmp (infos->namespace, target_namespace) != 0) {
1619  continue;
1620  }
1621 
1622  /* Rebind coll to ref'd operand directly, effectively strip reference */
1623  /* FIXME: Do we really need to do this _clear? */
1625 
1626  xmmsv_list_iter_remove (iter);
1627 
1628  tmp = xmmsv_new_coll (infos->oldtarget);
1629  xmmsv_list_iter_insert (iter, tmp);
1630  xmmsv_unref (tmp);
1631  }
1633 }
1634 
1635 /**
1636  * Check if the current operator is a reference to a given collection,
1637  * and if so, update the structure passed as userdata.
1638  */
1639 static void
1640 check_for_reference (xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
1641 {
1642  coll_refcheck_t *check = (coll_refcheck_t*)udata;
1643  if (xmmsv_coll_get_type (coll) == XMMS_COLLECTION_TYPE_REFERENCE && !check->found) {
1644  gchar *target_name, *target_namespace;
1645 
1646  xmmsv_coll_attribute_get (coll, "reference", &target_name);
1647  xmmsv_coll_attribute_get (coll, "namespace", &target_namespace);
1648  if (strcmp (check->target_name, target_name) == 0 &&
1649  strcmp (check->target_namespace, target_namespace) == 0) {
1650  check->found = TRUE;
1651  } else {
1652  xmmsv_coll_t *op;
1653  xmmsv_t *tmp;
1654 
1655  if (xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) {
1656  xmmsv_get_coll (tmp, &op);
1657  xmms_collection_apply_to_collection_recurs (dag, op, coll,
1658  check_for_reference,
1659  udata);
1660  }
1661  }
1662  }
1663 }
1664 
1665 
1666 /** Forwarding function to fix type warnings.
1667  *
1668  * @param coll The collection to unref.
1669  */
1670 static void
1671 coll_unref (void *coll)
1672 {
1673  xmmsv_coll_unref (coll);
1674 }
1675 
1676 
1677 
1678 /* ============ FIND / COLLECTION MATCH FUNCTIONS ============ */
1679 
1680 /* Generate a build_match hashtable, states initialized to UNCHECKED. */
1681 static void
1682 build_match_table (gpointer key, gpointer value, gpointer udata)
1683 {
1684  GHashTable *match_table = udata;
1685  coll_find_state_t *match = g_new (coll_find_state_t, 1);
1687  g_hash_table_replace (match_table, g_strdup (key), match);
1688 }
1689 
1690 /* Return the first unchecked element from the match_table, set the
1691  * udata pointer to contain the key of that element.
1692  */
1693 static gboolean
1694 find_unchecked (gpointer name, gpointer value, gpointer udata)
1695 {
1696  coll_find_state_t *match = value;
1697  gchar **open = udata;
1698  *open = name;
1699  return (*match == XMMS_COLLECTION_FIND_STATE_UNCHECKED);
1700 }
1701 
1702 /* Build a list of all matched entries of the match_table in the udata
1703  * pointer.
1704  */
1705 static void
1706 build_list_matches (gpointer key, gpointer value, gpointer udata)
1707 {
1708  gchar *coll_name = key;
1709  coll_find_state_t *state = value;
1710  GList **list = udata;
1711  if (*state == XMMS_COLLECTION_FIND_STATE_MATCH) {
1712  *list = g_list_prepend (*list, xmmsv_new_string (coll_name));
1713  }
1714 }
1715 
1716 /** Determine whether the mediainfos match the given collection.
1717  *
1718  * @param dag The collection DAG.
1719  * @param mediainfo The properties of the media to match against.
1720  * @param coll The collection to match with the mediainfos.
1721  * @param nsid The namespace id of the collection.
1722  * @param match_table The match_table for all collections in that namespace.
1723  * @return TRUE if the collection matches, FALSE otherwise.
1724  */
1725 static gboolean
1726 xmms_collection_media_match (xmms_coll_dag_t *dag, GHashTable *mediainfo,
1727  xmmsv_coll_t *coll, guint nsid,
1728  GHashTable *match_table)
1729 {
1730  gboolean match = FALSE;
1731  xmmsv_coll_t *op;
1732  gchar *attr1 = NULL, *attr2 = NULL;
1733  xmmsv_t *val;
1734  xmms_medialib_entry_t entry, id;
1735  xmmsv_list_iter_t *iter;
1736 
1737  switch (xmmsv_coll_get_type (coll)) {
1739  if (xmmsv_coll_attribute_get (coll, "reference", &attr1)) {
1740  if (strcmp (attr1, "All Media") == 0) {
1741  match = TRUE;
1742  } else if (xmmsv_coll_attribute_get (coll, "namespace", &attr2)) {
1743  match = xmms_collection_media_match_reference (dag, mediainfo,
1744  coll, nsid,
1745  match_table,
1746  attr1, attr2);
1747  }
1748  }
1749  break;
1750 
1752  /* if ANY matches */
1754 
1755  for (xmmsv_list_iter_first (iter);
1756  !match && xmmsv_list_iter_valid (iter);
1757  xmmsv_list_iter_next (iter)) {
1758 
1759  xmmsv_list_iter_entry (iter, &val);
1760  xmmsv_get_coll (val, &op);
1761 
1762  match = xmms_collection_media_match (dag, mediainfo, op,
1763  nsid, match_table);
1764  }
1766  break;
1767 
1769  /* if ALL match */
1770  match = TRUE;
1772 
1773  for (xmmsv_list_iter_first (iter);
1774  match && xmmsv_list_iter_valid (iter);
1775  xmmsv_list_iter_next (iter)) {
1776 
1777  xmmsv_list_iter_entry (iter, &val);
1778  xmmsv_get_coll (val, &op);
1779 
1780  match = xmms_collection_media_match (dag, mediainfo, op,
1781  nsid, match_table);
1782  }
1784  break;
1785 
1787  /* invert result from operand */
1788  match = !xmms_collection_media_match_operand (dag, mediainfo, coll,
1789  nsid, match_table);
1790  break;
1791 
1793  match = xmms_collection_media_filter_has (dag, mediainfo, coll,
1794  nsid, match_table);
1795  break;
1796 
1798  match = xmms_collection_media_filter_equals (dag, mediainfo, coll,
1799  nsid, match_table);
1800  break;
1801 
1803  match = xmms_collection_media_filter_match (dag, mediainfo, coll,
1804  nsid, match_table);
1805  break;
1806 
1808  match = xmms_collection_media_filter_smaller (dag, mediainfo, coll,
1809  nsid, match_table);
1810  break;
1811 
1813  match = xmms_collection_media_filter_greater (dag, mediainfo, coll,
1814  nsid, match_table);
1815  break;
1816 
1820  /* check if id in idlist */
1821  val = g_hash_table_lookup (mediainfo, "id");
1822  if (val != NULL) {
1823  xmmsv_get_int (val, &id);
1824 
1826  for (xmmsv_list_iter_first (iter);
1827  xmmsv_list_iter_valid (iter);
1828  xmmsv_list_iter_next (iter)) {
1829 
1830  xmmsv_list_iter_entry_int (iter, &entry);
1831  if (entry == id) {
1832  match = TRUE;
1833  break;
1834  }
1835  }
1837  }
1838  break;
1839 
1840  /* invalid type */
1841  default:
1842  XMMS_DBG ("invalid collection operator in xmms_collection_media_match");
1843  g_assert_not_reached ();
1844  break;
1845  }
1846 
1847  return match;
1848 }
1849 
1850 /** Determine whether the mediainfos match the given reference operator.
1851  *
1852  * @param dag The collection DAG.
1853  * @param mediainfo The properties of the media to match against.
1854  * @param coll The collection (ref op) to match with the mediainfos.
1855  * @param nsid The namespace id of the collection.
1856  * @param match_table The match_table for all collections in that namespace.
1857  * @param refname The name of the referenced collection.
1858  * @param refns The namespace of the referenced collection.
1859  * @return TRUE if the collection matches, FALSE otherwise.
1860  */
1861 static gboolean
1862 xmms_collection_media_match_reference (xmms_coll_dag_t *dag, GHashTable *mediainfo,
1863  xmmsv_coll_t *coll, guint nsid,
1864  GHashTable *match_table,
1865  const gchar *refname, const gchar *refns)
1866 {
1867  gboolean match;
1868  guint refnsid;
1869  coll_find_state_t *matchstate;
1870 
1871  /* Same NS, should be in the match table */
1872  refnsid = xmms_collection_get_namespace_id (refns);
1873  if (refnsid == nsid) {
1874  matchstate = g_hash_table_lookup (match_table, refname);
1875  if (*matchstate == XMMS_COLLECTION_FIND_STATE_UNCHECKED) {
1876  /* Check ref'd collection match status and save it */
1877  matchstate = g_new (coll_find_state_t, 1);
1878  match = xmms_collection_media_match_operand (dag,
1879  mediainfo,
1880  coll, nsid,
1881  match_table);
1882 
1883  if (match) {
1884  *matchstate = XMMS_COLLECTION_FIND_STATE_MATCH;
1885  } else {
1886  *matchstate = XMMS_COLLECTION_FIND_STATE_NOMATCH;
1887  }
1888 
1889  g_hash_table_replace (match_table, g_strdup (refname), matchstate);
1890 
1891  } else {
1892  match = (*matchstate == XMMS_COLLECTION_FIND_STATE_MATCH);
1893  }
1894 
1895  /* In another NS, just check if it matches */
1896  } else {
1897  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
1898  nsid, match_table);
1899  }
1900 
1901  return match;
1902 }
1903 
1904 /** Determine whether the mediainfos match the first operand of the
1905  * given operator.
1906  *
1907  * @param dag The collection DAG.
1908  * @param mediainfo The properties of the media to match against.
1909  * @param coll Match the mediainfos with the operand of that collection.
1910  * @param nsid The namespace id of the collection.
1911  * @param match_table The match_table for all collections in that namespace.
1912  * @return TRUE if the collection matches, FALSE otherwise.
1913  */
1914 static gboolean
1915 xmms_collection_media_match_operand (xmms_coll_dag_t *dag, GHashTable *mediainfo,
1916  xmmsv_coll_t *coll, guint nsid,
1917  GHashTable *match_table)
1918 {
1919  xmmsv_coll_t *op;
1920  xmmsv_t *tmp;
1921  gboolean match = FALSE;
1922 
1923  if (xmmsv_list_get (xmmsv_coll_operands_get (coll), 0, &tmp)) {
1924  xmmsv_get_coll (tmp, &op);
1925 
1926  match = xmms_collection_media_match (dag, mediainfo, op, nsid, match_table);
1927  }
1928 
1929  return match;
1930 }
1931 
1932 /** Get all the properties for the given media.
1933  *
1934  * @param mid The id of the media.
1935  * @return A HashTable with all the properties.
1936  */
1937 static GHashTable *
1938 xmms_collection_media_info (xmms_medialib_entry_t mid, xmms_error_t *err)
1939 {
1940  GList *res;
1941  GList *n;
1942  GHashTable *infos;
1943  gchar *name;
1944  const gchar *buf;
1945  xmmsv_t *cmdval;
1946  xmmsv_t *value;
1947  guint state;
1948 
1949  /* FIXME: could probably reuse tree from medialib_info directly. ignores sources? */
1950  res = xmms_medialib_info_list (NULL, mid, err);
1951 
1952  /* Transform the list into a HashMap */
1953  infos = g_hash_table_new_full (g_str_hash, g_str_equal,
1954  g_free, (GDestroyNotify) xmmsv_unref);
1955  for (state = 0, n = res; n; state = (state + 1) % 3, n = n->next) {
1956  switch (state) {
1957  case 0: /* source */
1958  break;
1959 
1960  case 1: /* prop name */
1961  cmdval = n->data;
1962  xmmsv_get_string (cmdval, &buf);
1963  name = g_strdup (buf);
1964  break;
1965 
1966  case 2: /* prop value */
1967  value = xmmsv_ref (n->data);
1968 
1969  /* Only insert the first source */
1970  if (g_hash_table_lookup (infos, name) == NULL) {
1971  g_hash_table_replace (infos, name, value);
1972  }
1973  break;
1974  }
1975 
1976  xmmsv_unref (n->data);
1977  }
1978 
1979  g_list_free (res);
1980 
1981  return infos;
1982 }
1983 
1984 /** Get the string associated to the property of the mediainfo
1985  * identified by the "field" attribute of the collection.
1986  *
1987  * @return The property value as a string.
1988  */
1989 static gboolean
1990 filter_get_mediainfo_field_string (xmmsv_coll_t *coll,
1991  GHashTable *mediainfo, gchar **val)
1992 {
1993  gboolean retval = FALSE;
1994  gchar *attr;
1995  xmmsv_t *cmdval;
1996 
1997  if (xmmsv_coll_attribute_get (coll, "field", &attr)) {
1998  cmdval = g_hash_table_lookup (mediainfo, attr);
1999  if (cmdval != NULL) {
2000  switch (xmmsv_get_type (cmdval)) {
2001  case XMMSV_TYPE_STRING:
2002  {
2003  const gchar *s;
2004  xmmsv_get_string (cmdval, &s);
2005  *val = g_strdup (s);
2006  retval = TRUE;
2007  break;
2008  }
2009  case XMMSV_TYPE_INT32:
2010  {
2011  gint i;
2012  xmmsv_get_int (cmdval, &i);
2013  *val = g_strdup_printf ("%d", i);
2014  retval = TRUE;
2015  break;
2016  }
2017  default:
2018  break;
2019  }
2020  }
2021  }
2022 
2023  return retval;
2024 }
2025 
2026 /** Get the integer associated to the property of the mediainfo
2027  * identified by the "field" attribute of the collection.
2028  *
2029  * @return The property value as an integer.
2030  */
2031 static gboolean
2032 filter_get_mediainfo_field_int (xmmsv_coll_t *coll, GHashTable *mediainfo, gint *val)
2033 {
2034  gboolean retval = FALSE;
2035  gchar *attr;
2036  xmmsv_t *cmdval;
2037 
2038  if (xmmsv_coll_attribute_get (coll, "field", &attr)) {
2039  cmdval = g_hash_table_lookup (mediainfo, attr);
2040  if (cmdval != NULL && xmmsv_get_type (cmdval) == XMMSV_TYPE_INT32) {
2041  xmmsv_get_int (cmdval, val);
2042  retval = TRUE;
2043  }
2044  }
2045 
2046  return retval;
2047 }
2048 
2049 /* Get the string value of the "value" attribute of the collection. */
2050 static gboolean
2051 filter_get_operator_value_string (xmmsv_coll_t *coll, const gchar **val)
2052 {
2053  gchar *attr;
2054  gboolean valid;
2055 
2056  valid = xmmsv_coll_attribute_get (coll, "value", &attr);
2057  if (valid) {
2058  *val = attr;
2059  }
2060 
2061  return valid;
2062 }
2063 
2064 /* Get the integer value of the "value" attribute of the collection. */
2065 static gboolean
2066 filter_get_operator_value_int (xmmsv_coll_t *coll, gint *val)
2067 {
2068  gint buf;
2069  gboolean valid;
2070 
2071  valid = xmms_collection_get_int_attr (coll, "value", &buf);
2072  if (valid) {
2073  *val = buf;
2074  }
2075 
2076  return valid;
2077 }
2078 
2079 /* Check whether the given operator has the "case-sensitive" attribute
2080  * or not. */
2081 static gboolean
2082 filter_get_operator_case (xmmsv_coll_t *coll, gboolean *val)
2083 {
2084  gchar *attr;
2085 
2086  if (xmmsv_coll_attribute_get (coll, "case-sensitive", &attr)) {
2087  *val = (strcmp (attr, "true") == 0);
2088  }
2089  else {
2090  *val = FALSE;
2091  }
2092 
2093  return TRUE;
2094 }
2095 
2096 /* Check whether the HAS filter operator matches the mediainfo. */
2097 static gboolean
2098 xmms_collection_media_filter_has (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2099  xmmsv_coll_t *coll, guint nsid,
2100  GHashTable *match_table)
2101 {
2102  gboolean match = FALSE;
2103  gchar *mediaval;
2104 
2105  /* If operator matches, recurse upwards in the operand */
2106  if (filter_get_mediainfo_field_string (coll, mediainfo, &mediaval)) {
2107  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2108  nsid, match_table);
2109 
2110  g_free (mediaval);
2111  }
2112 
2113  return match;
2114 }
2115 
2116 /* Check whether the MATCH filter operator matches the mediainfo. */
2117 static gboolean
2118 xmms_collection_media_filter_equals (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2119  xmmsv_coll_t *coll, guint nsid,
2120  GHashTable *match_table)
2121 {
2122  gboolean match = FALSE;
2123  gchar *mediaval = NULL;
2124  const gchar *opval;
2125  gboolean case_sens;
2126 
2127  if (filter_get_mediainfo_field_string (coll, mediainfo, &mediaval) &&
2128  filter_get_operator_value_string (coll, &opval) &&
2129  filter_get_operator_case (coll, &case_sens)) {
2130 
2131  if (case_sens) {
2132  match = (strcmp (mediaval, opval) == 0);
2133  } else {
2134  match = (g_ascii_strcasecmp (mediaval, opval) == 0);
2135  }
2136  }
2137 
2138  /* If operator matches, recurse upwards in the operand */
2139  if (match) {
2140  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2141  nsid, match_table);
2142  }
2143 
2144  if (mediaval != NULL) {
2145  g_free (mediaval);
2146  }
2147 
2148  return match;
2149 }
2150 
2151 /* Check whether the MATCH filter operator matches the mediainfo. */
2152 static gboolean
2153 xmms_collection_media_filter_match (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2154  xmmsv_coll_t *coll, guint nsid,
2155  GHashTable *match_table)
2156 {
2157  gboolean match = FALSE;
2158  gchar *buf, *opval, *mediaval;
2159  const gchar *s;
2160  gboolean case_sens;
2161 
2162  if (filter_get_mediainfo_field_string (coll, mediainfo, &buf) &&
2163  filter_get_operator_value_string (coll, &s) &&
2164  filter_get_operator_case (coll, &case_sens)) {
2165 
2166  /* Prepare values */
2167  if (case_sens) {
2168  opval = g_strdup (s);
2169  mediaval = g_strdup (buf);
2170  } else {
2171  opval = g_utf8_strdown (s, -1);
2172  mediaval = g_utf8_strdown (buf, -1);
2173  }
2174 
2175  match = g_pattern_match_simple (opval, mediaval);
2176 
2177  g_free (buf);
2178  g_free (opval);
2179  g_free (mediaval);
2180 
2181  /* If operator matches, recurse upwards in the operand */
2182  if (match) {
2183  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2184  nsid, match_table);
2185  }
2186  }
2187 
2188  return match;
2189 }
2190 
2191 /* Check whether the SMALLER filter operator matches the mediainfo. */
2192 static gboolean
2193 xmms_collection_media_filter_smaller (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2194  xmmsv_coll_t *coll, guint nsid,
2195  GHashTable *match_table)
2196 {
2197  gboolean match = FALSE;
2198  gint mediaval;
2199  gint opval;
2200 
2201  /* If operator matches, recurse upwards in the operand */
2202  if (filter_get_mediainfo_field_int (coll, mediainfo, &mediaval) &&
2203  filter_get_operator_value_int (coll, &opval) &&
2204  (mediaval < opval) ) {
2205 
2206  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2207  nsid, match_table);
2208  }
2209 
2210  return match;
2211 }
2212 
2213 /* Check whether the GREATER filter operator matches the mediainfo. */
2214 static gboolean
2215 xmms_collection_media_filter_greater (xmms_coll_dag_t *dag, GHashTable *mediainfo,
2216  xmmsv_coll_t *coll, guint nsid,
2217  GHashTable *match_table)
2218 {
2219  gboolean match = FALSE;
2220  gint mediaval;
2221  gint opval;
2222 
2223  /* If operator matches, recurse upwards in the operand */
2224  if (filter_get_mediainfo_field_int (coll, mediainfo, &mediaval) &&
2225  filter_get_operator_value_int (coll, &opval) &&
2226  (mediaval > opval) ) {
2227 
2228  match = xmms_collection_media_match_operand (dag, mediainfo, coll,
2229  nsid, match_table);
2230  }
2231 
2232  return match;
2233 }
void xmmsv_list_iter_first(xmmsv_list_iter_t *it)
Rewind the iterator to the start of the list.
Definition: value.c:1523
G_BEGIN_DECLS typedef gint32 xmms_medialib_entry_t
Definition: xmms_medialib.h:86
struct xmmsv_list_iter_St xmmsv_list_iter_t
Definition: xmmsv_list.h:69
coll_find_state_t
Definition: collection.c:73
#define XMMS_OBJECT(p)
Definition: xmms_object.h:77
xmms_medialib_entry_t xmms_collection_get_random_media(xmms_coll_dag_t *dag, xmmsv_coll_t *source)
Get a random media entry from the given collection.
Definition: collection.c:978
const gchar * xmms_collection_get_namespace_string(xmms_collection_namespace_id_t nsid)
Find the namespace name (string) corresponding to a namespace id.
Definition: collection.c:1320
void xmmsv_unref(xmmsv_t *val)
Decreases the references for the xmmsv_t When the number of references reaches 0 it will be freed...
Definition: value.c:303
#define xmms_object_unref(obj)
Definition: xmms_object.h:109
int xmmsv_list_append(xmmsv_t *listv, xmmsv_t *val)
Append an element to the end of the list xmmsv_t.
Definition: value.c:1340
int xmmsv_coll_attribute_get(xmmsv_coll_t *coll, const char *key, char **value)
Retrieve the value of the attribute of the given collection.
Definition: coll.c:498
int xmmsv_list_clear(xmmsv_t *listv)
Empty the list from all its elements.
Definition: value.c:1356
void bind_all_references(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
If a reference, add the operator of the pointed collection as an operand.
Definition: collection.c:1503
xmms_collection_namespace_id_t xmms_collection_get_namespace_id(const gchar *namespace)
Find the namespace id corresponding to a namespace string.
Definition: collection.c:1297
int xmmsv_coll_idlist_append(xmmsv_coll_t *coll, int id)
Append a value to the idlist.
Definition: coll.c:252
#define xmms_error_iserror(e)
Definition: xmms_error.h:57
struct xmms_stream_type_St xmms_stream_type_t
int xmmsv_list_iter_remove(xmmsv_list_iter_t *it)
Remove the element in the list at the position pointed at by the iterator.
Definition: value.c:1652
struct xmmsv_St xmmsv_t
Definition: xmmsv_general.h:48
void xmmsv_coll_attribute_set(xmmsv_coll_t *coll, const char *key, const char *value)
Set an attribute in the given collection.
Definition: coll.c:460
xmmsv_t * xmmsv_new_string(const char *s)
Allocates a new string xmmsv_t.
Definition: value.c:180
struct xmms_xform_St xmms_xform_t
xmmsv_coll_t * xmmsv_coll_ref(xmmsv_coll_t *coll)
Increases the references for the xmmsv_coll_t.
Definition: coll.c:61
void(* FuncApplyToColl)(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, xmmsv_coll_t *parent, void *udata)
int xmmsv_get_int(const xmmsv_t *val, int32_t *r)
Retrieves a signed integer from the value.
Definition: value.c:823
void xmms_coll_sync_init(xmms_coll_dag_t *dag)
Get the collection-to-database-synchronization thread running.
Definition: collsync.c:84
xmmsv_coll_t * xmms_collection_get_pointer(xmms_coll_dag_t *dag, const gchar *collname, guint nsid)
Find the collection structure corresponding to the given name in the given namespace.
Definition: collection.c:873
#define XMMS_MAX_INT_ATTRIBUTE_LEN
Definition: xmms_playlist.h:36
gboolean check_string_list(xmmsv_t *list)
Checks that the list only contains string values.
Definition: object.c:469
void xmms_collection_changed_msg_send(xmms_coll_dag_t *colldag, GTree *dict)
Definition: collection.c:163
int xmmsv_get_list_iter(const xmmsv_t *val, xmmsv_list_iter_t **it)
Retrieves a list iterator from a list xmmsv_t.
Definition: value.c:926
gboolean xmms_medialib_entry_property_set_int_source(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property, gint value, guint32 source)
Definition: medialib.c:582
xmmsv_coll_type_t
guint32 xmms_medialib_source_to_id(xmms_medialib_session_t *session, const gchar *source)
Definition: medialib.c:261
GList * xmms_medialib_select(xmms_medialib_session_t *session, const gchar *query, xmms_error_t *error)
Get a list of GHashTables &#39;s that matches the query.
Definition: medialib.c:1380
xmms_xform_t * xmms_xform_chain_setup_url(xmms_medialib_entry_t entry, const gchar *url, GList *goal_formats, gboolean rehash)
Definition: xform.c:1405
GList * xmms_xform_browse_method(xmms_xform_t *xform, const gchar *url, xmms_error_t *error)
Definition: xform.c:257
struct xmmsv_St * xmmsv_coll_operands_get(xmmsv_coll_t *coll)
Definition: coll.c:437
gboolean xmms_collection_get_int_attr(xmmsv_coll_t *coll, const gchar *attrname, gint *val)
Extract an attribute from a collection as an integer.
Definition: collection.c:898
int xmmsv_dict_foreach(xmmsv_t *dictv, xmmsv_dict_foreach_func func, void *user_data)
Apply a function to each key-element pair in the list.
Definition: value.c:1853
GList * xmms_collection_query_ids(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, gint32 lim_start, gint32 lim_len, xmmsv_t *order, xmms_error_t *err)
Find the ids of the media matched by a collection.
Definition: collection.c:731
struct xmmsv_St * xmmsv_coll_idlist_get(xmmsv_coll_t *coll)
Return the list of ids stored in the collection.
Definition: coll.c:429
xmms_stream_type_t * _xmms_stream_type_new(const gchar *begin,...)
Definition: streamtype.c:362
xmms_collection_namespace_id_t
#define xmms_log_error(fmt,...)
Definition: xmms_log.h:35
#define XMMS_COLLECTION_CHANGED_MSG(type, name, namespace)
Definition: collection.c:176
#define XMMS_COLLECTION_NS_PLAYLISTS
int xmmsv_dict_get(xmmsv_t *dictv, const char *key, xmmsv_t **val)
Get the element corresponding to the given key in the dict xmmsv_t (if it exists).
Definition: value.c:1717
GList * xmms_medialib_info_list(xmms_medialib_t *medialib, guint32 id, xmms_error_t *err)
Definition: medialib.c:1182
void xmms_object_emit_f(xmms_object_t *object, guint32 signalid, xmmsv_type_t type,...)
Emits a signal on the current object.
Definition: object.c:256
void xmmsv_list_iter_next(xmmsv_list_iter_t *it)
Advance the iterator to the next element in the list.
Definition: value.c:1553
xmmsv_t * xmmsv_new_list(void)
Allocates a new list xmmsv_t.
Definition: value.c:250
void xmmsv_list_iter_explicit_destroy(xmmsv_list_iter_t *it)
Explicitly free list iterator.
Definition: value.c:1478
xmms_medialib_entry_t xmms_medialib_entry_new_encoded(xmms_medialib_session_t *session, const char *url, xmms_error_t *error)
Definition: medialib.c:952
struct xmmsv_coll_St xmmsv_coll_t
Definition: xmmsv_coll.h:28
int xmmsv_list_get(xmmsv_t *listv, int pos, xmmsv_t **val)
Get the element at the given position in the list xmmsv_t.
Definition: value.c:1218
GString * xmms_collection_get_query(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, guint limit_start, guint limit_len, xmmsv_t *order, xmmsv_t *fetch, xmmsv_t *group)
Definition: collquery.c:97
#define XMMS_STREAM_TYPE_BEGIN
struct xmms_playlist_St xmms_playlist_t
Definition: xmms_playlist.h:41
void xmms_collection_apply_to_all_collections(xmms_coll_dag_t *dag, FuncApplyToColl f, void *udata)
Apply a function of type FuncApplyToColl to all the collections in all namespaces.
Definition: collection.c:1393
void xmms_collection_dag_save(xmms_coll_dag_t *dag)
Save the collection DAG in the database.
Definition: collserial.c:53
struct add_metadata_from_tree_user_data_St add_metadata_from_tree_user_data_t
xmmsv_t * xmmsv_new_coll(xmmsv_coll_t *coll)
Allocates a new collection xmmsv_t.
Definition: value.c:202
void xmms_collection_update_pointer(xmms_coll_dag_t *dag, const gchar *name, guint nsid, xmmsv_coll_t *newtarget)
Update a reference to point to a new collection.
Definition: collection.c:849
xmmsv_coll_t * xmmsv_coll_new(xmmsv_coll_type_t type)
Allocate a new collection of the given type.
Definition: coll.c:78
xmmsv_type_t xmmsv_get_type(const xmmsv_t *val)
Get the type of the value.
Definition: value.c:392
xmms_collection_changed_actions_t
xmmsv_t * xmmsv_new_int(int32_t i)
Allocates a new integer xmmsv_t.
Definition: value.c:161
gboolean xmms_collection_set_int_attr(xmmsv_coll_t *coll, const gchar *attrname, gint newval)
Set the attribute of a collection as an integer.
Definition: collection.c:926
void xmmsv_coll_add_operand(xmmsv_coll_t *coll, xmmsv_coll_t *op)
Add the operand to the given collection.
Definition: coll.c:195
int xmmsv_list_get_size(xmmsv_t *listv)
Return the size of the list.
Definition: value.c:1403
xmmsv_t * xmmsv_ref(xmmsv_t *val)
References the xmmsv_t.
Definition: value.c:288
void xmms_collection_dag_replace(xmms_coll_dag_t *dag, xmms_collection_namespace_id_t nsid, gchar *key, xmmsv_coll_t *newcoll)
Update the DAG to update the value of the pair with the given key.
Definition: collection.c:858
#define XMMS_ACTIVE_PLAYLIST
const gchar * xmms_collection_find_alias(xmms_coll_dag_t *dag, guint nsid, xmmsv_coll_t *value, const gchar *key)
Reverse-search the list of collections in the given namespace to find the first pair whose value matc...
Definition: collection.c:955
void xmmsv_coll_remove_operand(xmmsv_coll_t *coll, xmmsv_coll_t *op)
Remove all the occurences of the operand in the given collection.
Definition: coll.c:226
struct xmms_medialib_session_St xmms_medialib_session_t
Definition: xmms_medialib.h:87
void xmms_coll_sync_shutdown()
Shutdown the collection-to-database-synchronization thread.
Definition: collsync.c:96
#define XMMS_PLAYLIST_COLLECTION_CHANGED_MSG(playlist, name)
Definition: xmms_playlist.h:68
void xmms_collection_apply_to_collection(xmms_coll_dag_t *dag, xmmsv_coll_t *coll, FuncApplyToColl f, void *udata)
Apply a function of type FuncApplyToColl to the given collection.
Definition: collection.c:1412
#define XMMS_COLLECTION_NUM_NAMESPACES
void xmms_collection_dag_restore(xmms_coll_dag_t *dag)
Restore the collection DAG from the database.
Definition: collserial.c:85
void xmms_coll_sync_schedule_sync()
Schedule a collection-to-database-synchronization in 10 seconds.
Definition: collsync.c:113
#define XMMS_DBG(fmt,...)
Definition: xmms_log.h:32
#define xmms_medialib_begin_write()
#define xmms_medialib_begin()
void xmms_object_connect(xmms_object_t *object, guint32 signalid, xmms_object_handler_t handler, gpointer userdata)
Connect to a signal that is emitted by this object.
Definition: object.c:115
xmms_coll_dag_t * xmms_collection_init(xmms_playlist_t *playlist)
Initializes a new xmms_coll_dag_t.
Definition: collection.c:214
int xmmsv_list_iter_valid(xmmsv_list_iter_t *it)
Check whether the iterator is valid and points to a valid element.
Definition: value.c:1512
gboolean xmms_medialib_entry_property_set_str_source(xmms_medialib_session_t *session, xmms_medialib_entry_t entry, const gchar *property, const gchar *value, guint32 source)
Definition: medialib.c:632
int xmmsv_get_string(const xmmsv_t *val, const char **r)
Retrieves a string from the value.
Definition: value.c:863
int xmmsv_list_iter_insert(xmmsv_list_iter_t *it, xmmsv_t *val)
Insert an element in the list at the position pointed at by the iterator.
Definition: value.c:1636
void xmms_collection_foreach_in_namespace(xmms_coll_dag_t *dag, guint nsid, GHFunc f, void *udata)
Apply a function to all the collections in a given namespace.
Definition: collection.c:1373
#define xmms_object_new(objtype, destroyfunc)
Definition: xmms_object.h:115
G_BEGIN_DECLS struct xmms_error_St xmms_error_t
int xmmsv_list_iter_entry(xmmsv_list_iter_t *it, xmmsv_t **val)
Get the element currently pointed at by the iterator.
Definition: value.c:1495
int xmmsv_list_iter_entry_int(xmmsv_list_iter_t *it, int32_t *val)
#define XMMS_COLLECTION_NS_COLLECTIONS
void xmms_collection_sync(xmms_coll_dag_t *dag)
Synchronize collection data to the database (i.e.
Definition: collection.c:539
int xmmsv_dict_remove(xmmsv_t *dictv, const char *key)
Remove the element corresponding to a given key in the dict xmmsv_t (if it exists).
Definition: value.c:1803
xmmsv_coll_type_t xmmsv_coll_get_type(xmmsv_coll_t *coll)
Return the type of the collection.
Definition: coll.c:367
void xmms_medialib_end(xmms_medialib_session_t *session)
Definition: medialib.c:425
#define XMMS_COLLECTION_NS_ALL
GTree * xmms_collection_changed_msg_new(xmms_collection_changed_actions_t type, const gchar *plname, const gchar *namespace)
Definition: collection.c:147
int xmmsv_get_coll(const xmmsv_t *val, xmmsv_coll_t **coll)
Retrieves a collection from the value.
Definition: value.c:883
struct xmms_coll_dag_St xmms_coll_dag_t
void xmmsv_coll_unref(xmmsv_coll_t *coll)
Decreases the references for the xmmsv_coll_t When the number of references reaches 0 it will be free...
Definition: coll.c:140