~ubuntu-branches/ubuntu/karmic/mergeant/karmic

« back to all changes in this revision

Viewing changes to libmergeant/mg-join.c

  • Committer: Bazaar Package Importer
  • Author(s): Gustavo R. Montesino
  • Date: 2007-11-29 08:44:48 UTC
  • mfrom: (2.1.4 hardy)
  • Revision ID: james.westby@ubuntu.com-20071129084448-6aon73d22bv6hzfw
Tags: 0.67-3
* Re-enable installation of the mime files in mergeant.install
* mergeant.dirs: create usr/share/mime/packages to make dh_installmime add
  the update-mime-database code snippets

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* mg-join.c
2
 
 *
3
 
 * Copyright (C) 2003 Vivien Malerba
4
 
 *
5
 
 * This program is free software; you can redistribute it and/or
6
 
 * modify it under the terms of the GNU General Public License as
7
 
 * published by the Free Software Foundation; either version 2 of the
8
 
 * License, or (at your option) any later version.
9
 
 *
10
 
 * This program is distributed in the hope that it will be useful,
11
 
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 
 * GNU General Public License for more details.
14
 
 *
15
 
 * You should have received a copy of the GNU General Public License
16
 
 * along with this program; if not, write to the Free Software
17
 
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
18
 
 * USA
19
 
 */
20
 
 
21
 
#include "mg-join.h"
22
 
#include "mg-query.h"
23
 
#include "mg-ref-base.h"
24
 
#include "mg-target.h"
25
 
#include "mg-xml-storage.h"
26
 
#include "mg-referer.h"
27
 
#include "marshal.h"
28
 
#include "mg-condition.h"
29
 
#include <string.h>
30
 
 
31
 
/* 
32
 
 * Main static functions 
33
 
 */
34
 
static void mg_join_class_init (MgJoinClass * class);
35
 
static void mg_join_init (MgJoin * srv);
36
 
static void mg_join_dispose (GObject   * object);
37
 
static void mg_join_finalize (GObject   * object);
38
 
 
39
 
static void mg_join_set_property (GObject              *object,
40
 
                                    guint                 param_id,
41
 
                                    const GValue         *value,
42
 
                                    GParamSpec           *pspec);
43
 
static void mg_join_get_property (GObject              *object,
44
 
                                    guint                 param_id,
45
 
                                    GValue               *value,
46
 
                                    GParamSpec           *pspec);
47
 
 
48
 
/* XML storage interface */
49
 
static void        mg_join_xml_storage_init (MgXmlStorageIface *iface);
50
 
static gchar      *mg_join_get_xml_id       (MgXmlStorage *iface);
51
 
static xmlNodePtr  mg_join_save_to_xml      (MgXmlStorage *iface, GError **error);
52
 
static gboolean    mg_join_load_from_xml    (MgXmlStorage *iface, xmlNodePtr node, GError **error);
53
 
 
54
 
/* Referer interface */
55
 
static void        mg_join_referer_init        (MgRefererIface *iface);
56
 
static gboolean    mg_join_activate            (MgReferer *iface);
57
 
static void        mg_join_deactivate          (MgReferer *iface);
58
 
static gboolean    mg_join_is_active           (MgReferer *iface);
59
 
static GSList     *mg_join_get_ref_objects     (MgReferer *iface);
60
 
static void        mg_join_replace_refs        (MgReferer *iface, GHashTable *replacements);
61
 
 
62
 
/* When the Query or any of the refering MgTarget is nullified */
63
 
static void        nullified_object_cb (GObject *obj, MgJoin *join);
64
 
static void        target_ref_lost_cb (MgRefBase *ref, MgJoin *join);
65
 
static void        target_removed_cb (MgQuery *query, MgTarget *target, MgJoin *join);
66
 
 
67
 
 
68
 
#ifdef debug
69
 
static void        mg_join_dump (MgJoin *join, guint offset);
70
 
#endif
71
 
 
72
 
/* get a pointer to the parents to be able to call their destructor */
73
 
static GObjectClass  *parent_class = NULL;
74
 
 
75
 
/* signals */
76
 
enum
77
 
{
78
 
        TYPE_CHANGED,
79
 
        CONDITION_CHANGED,
80
 
        LAST_SIGNAL
81
 
};
82
 
 
83
 
static gint mg_join_signals[LAST_SIGNAL] = { 0, 0 };
84
 
 
85
 
/* properties */
86
 
enum
87
 
{
88
 
        PROP_0,
89
 
        PROP
90
 
};
91
 
 
92
 
 
93
 
/* private structure */
94
 
struct _MgJoinPrivate
95
 
{
96
 
        MgJoinType   join_type;
97
 
        MgQuery     *query;
98
 
        MgRefBase   *target1;
99
 
        MgRefBase   *target2;
100
 
        MgCondition *cond;
101
 
};
102
 
 
103
 
 
104
 
 
105
 
/* module error */
106
 
GQuark mg_join_error_quark (void)
107
 
{
108
 
        static GQuark quark;
109
 
        if (!quark)
110
 
                quark = g_quark_from_static_string ("mg_join_error");
111
 
        return quark;
112
 
}
113
 
 
114
 
 
115
 
guint
116
 
mg_join_get_type (void)
117
 
{
118
 
        static GType type = 0;
119
 
 
120
 
        if (!type) {
121
 
                static const GTypeInfo info = {
122
 
                        sizeof (MgJoinClass),
123
 
                        (GBaseInitFunc) NULL,
124
 
                        (GBaseFinalizeFunc) NULL,
125
 
                        (GClassInitFunc) mg_join_class_init,
126
 
                        NULL,
127
 
                        NULL,
128
 
                        sizeof (MgJoin),
129
 
                        0,
130
 
                        (GInstanceInitFunc) mg_join_init
131
 
                };
132
 
 
133
 
                static const GInterfaceInfo xml_storage_info = {
134
 
                        (GInterfaceInitFunc) mg_join_xml_storage_init,
135
 
                        NULL,
136
 
                        NULL
137
 
                };
138
 
 
139
 
                static const GInterfaceInfo referer_info = {
140
 
                        (GInterfaceInitFunc) mg_join_referer_init,
141
 
                        NULL,
142
 
                        NULL
143
 
                };
144
 
                
145
 
                type = g_type_register_static (MG_BASE_TYPE, "MgJoin", &info, 0);
146
 
                g_type_add_interface_static (type, MG_XML_STORAGE_TYPE, &xml_storage_info);
147
 
                g_type_add_interface_static (type, MG_REFERER_TYPE, &referer_info);
148
 
        }
149
 
        return type;
150
 
}
151
 
 
152
 
static void 
153
 
mg_join_xml_storage_init (MgXmlStorageIface *iface)
154
 
{
155
 
        iface->get_xml_id = mg_join_get_xml_id;
156
 
        iface->save_to_xml = mg_join_save_to_xml;
157
 
        iface->load_from_xml = mg_join_load_from_xml;
158
 
}
159
 
 
160
 
static void
161
 
mg_join_referer_init (MgRefererIface *iface)
162
 
{
163
 
        iface->activate = mg_join_activate;
164
 
        iface->deactivate = mg_join_deactivate;
165
 
        iface->is_active = mg_join_is_active;
166
 
        iface->get_ref_objects = mg_join_get_ref_objects;
167
 
        iface->replace_refs = mg_join_replace_refs;
168
 
}
169
 
 
170
 
 
171
 
static void
172
 
mg_join_class_init (MgJoinClass * class)
173
 
{
174
 
        GObjectClass   *object_class = G_OBJECT_CLASS (class);
175
 
 
176
 
        parent_class = g_type_class_peek_parent (class);
177
 
 
178
 
        mg_join_signals[TYPE_CHANGED] =
179
 
                g_signal_new ("type_changed",
180
 
                              G_TYPE_FROM_CLASS (object_class),
181
 
                              G_SIGNAL_RUN_FIRST,
182
 
                              G_STRUCT_OFFSET (MgJoinClass, type_changed),
183
 
                              NULL, NULL,
184
 
                              marshal_VOID__VOID, G_TYPE_NONE,
185
 
                              0);
186
 
        mg_join_signals[CONDITION_CHANGED] =
187
 
                g_signal_new ("condition_changed",
188
 
                              G_TYPE_FROM_CLASS (object_class),
189
 
                              G_SIGNAL_RUN_FIRST,
190
 
                              G_STRUCT_OFFSET (MgJoinClass, condition_changed),
191
 
                              NULL, NULL,
192
 
                              marshal_VOID__VOID, G_TYPE_NONE,
193
 
                              0);
194
 
        class->type_changed = NULL;
195
 
        class->condition_changed = NULL;
196
 
 
197
 
        object_class->dispose = mg_join_dispose;
198
 
        object_class->finalize = mg_join_finalize;
199
 
 
200
 
        /* Properties */
201
 
        object_class->set_property = mg_join_set_property;
202
 
        object_class->get_property = mg_join_get_property;
203
 
        g_object_class_install_property (object_class, PROP,
204
 
                                         g_param_spec_pointer ("prop", NULL, NULL, 
205
 
                                                               (G_PARAM_READABLE | G_PARAM_WRITABLE)));
206
 
        /* virtual functions */
207
 
#ifdef debug
208
 
        MG_BASE_CLASS (class)->dump = (void (*)(MgBase *, guint)) mg_join_dump;
209
 
#endif
210
 
 
211
 
}
212
 
 
213
 
static void
214
 
mg_join_init (MgJoin * mg_join)
215
 
{
216
 
        mg_join->priv = g_new0 (MgJoinPrivate, 1);
217
 
        mg_join->priv->join_type = MG_JOIN_TYPE_INNER;
218
 
        mg_join->priv->query = NULL;
219
 
        mg_join->priv->target1 = NULL;
220
 
        mg_join->priv->target2 = NULL;
221
 
        mg_join->priv->cond = NULL;
222
 
}
223
 
 
224
 
/**
225
 
 * mg_join_new_with_targets
226
 
 * @query: a #MgQuery object in which the join will occur
227
 
 * @target_1: the 1st #MgTarget object participating in the join
228
 
 * @target_2: the 2nd #MgTarget object participating in the join
229
 
 *
230
 
 * Creates a new MgJoin object. Note: the #MgTarget ranks (1st and 2nd) does not matter, but
231
 
 * is necessary since the join may not be symetrical (LEFT or RIGHT join). Also, the #MgJoin object
232
 
 * may decide to swap the two if necessary.
233
 
 *
234
 
 * Returns: the new object
235
 
 */
236
 
GObject*
237
 
mg_join_new_with_targets (MgQuery *query, MgTarget *target_1, MgTarget *target_2)
238
 
{
239
 
        GObject   *obj;
240
 
        MgJoin *mg_join;
241
 
        MgConf *conf;
242
 
 
243
 
        g_return_val_if_fail (query && IS_MG_QUERY (query), NULL);
244
 
        g_return_val_if_fail (target_1 && IS_MG_TARGET (target_1), NULL);
245
 
        g_return_val_if_fail (target_2 && IS_MG_TARGET (target_2), NULL);
246
 
        g_return_val_if_fail (mg_target_get_query (target_1) == query, NULL);
247
 
        g_return_val_if_fail (mg_target_get_query (target_2) == query, NULL);
248
 
        g_return_val_if_fail (target_1 != target_2, NULL);
249
 
 
250
 
        conf = mg_base_get_conf (MG_BASE (query));
251
 
        obj = g_object_new (MG_JOIN_TYPE, "conf", conf, NULL);
252
 
        mg_join = MG_JOIN (obj);
253
 
        mg_base_set_id (MG_BASE (mg_join), 0);
254
 
 
255
 
        mg_join->priv->query = query;
256
 
        mg_join->priv->target1 = MG_REF_BASE (mg_ref_base_new (conf));
257
 
        mg_ref_base_set_ref_object (mg_join->priv->target1, MG_BASE (target_1));
258
 
 
259
 
        mg_join->priv->target2 = MG_REF_BASE (mg_ref_base_new (conf));
260
 
        mg_ref_base_set_ref_object (mg_join->priv->target2, MG_BASE (target_2));
261
 
        
262
 
        g_signal_connect (G_OBJECT (query), "nullified",
263
 
                          G_CALLBACK (nullified_object_cb), mg_join);
264
 
 
265
 
        g_signal_connect (G_OBJECT (query), "target_removed",
266
 
                          G_CALLBACK (target_removed_cb), mg_join);
267
 
 
268
 
        /* if the references to the any target is lost then we want to nullify the join */
269
 
        g_signal_connect (G_OBJECT (mg_join->priv->target1), "ref_lost",
270
 
                          G_CALLBACK (target_ref_lost_cb), mg_join);
271
 
        g_signal_connect (G_OBJECT (mg_join->priv->target2), "ref_lost",
272
 
                          G_CALLBACK (target_ref_lost_cb), mg_join);
273
 
 
274
 
        return obj;
275
 
}
276
 
 
277
 
/**
278
 
 * mg_join_new_with_xml_ids
279
 
 * @query: a #MgQuery object in which the join will occur
280
 
 * @target_1_xml_id: the 1st #MgTarget object's XML id participating in the join
281
 
 * @target_2_xml_id: the 2nd #MgTarget object's XML id participating in the join
282
 
 *
283
 
 * Creates a new MgJoin object. Note: the #MgTarget ranks (1st and 2nd) does not matter, but
284
 
 * is necessary since the join may not be symetrical (LEFT or RIGHT join). Also, the #MgJoin object
285
 
 * may decide to swap the two if necessary.
286
 
 *
287
 
 * Returns: the new object
288
 
 */
289
 
GObject *
290
 
mg_join_new_with_xml_ids (MgQuery *query, const gchar *target_1_xml_id, const gchar *target_2_xml_id)
291
 
{
292
 
        GObject   *obj;
293
 
        MgJoin *mg_join;
294
 
        MgConf *conf;
295
 
        gchar *qid, *ptr, *tok;
296
 
        gchar *tid;
297
 
        
298
 
        g_return_val_if_fail (query && IS_MG_QUERY (query), NULL);
299
 
        g_return_val_if_fail (target_1_xml_id && *target_1_xml_id, NULL);
300
 
        g_return_val_if_fail (target_2_xml_id && *target_2_xml_id, NULL);
301
 
        g_return_val_if_fail (strcmp (target_1_xml_id, target_2_xml_id), NULL);
302
 
 
303
 
        /* check that the XML Ids start with the query's XML Id */
304
 
        qid = mg_xml_storage_get_xml_id (MG_XML_STORAGE (query));
305
 
        tid = g_strdup (target_1_xml_id);
306
 
        ptr = strtok_r (tid, ":", &tok);
307
 
        g_return_val_if_fail (!strcmp (ptr, qid), NULL);
308
 
        g_free (tid);
309
 
        tid = g_strdup (target_2_xml_id);
310
 
        ptr = strtok_r (tid, ":", &tok);
311
 
        g_return_val_if_fail (!strcmp (ptr, qid), NULL);
312
 
        g_free (tid);
313
 
        g_free (qid);
314
 
        
315
 
 
316
 
        conf = mg_base_get_conf (MG_BASE (query));
317
 
        obj = g_object_new (MG_JOIN_TYPE, "conf", conf, NULL);
318
 
        mg_join = MG_JOIN (obj);
319
 
        mg_base_set_id (MG_BASE (mg_join), 0);
320
 
 
321
 
        mg_join->priv->query = query;
322
 
        mg_join->priv->target1 = MG_REF_BASE (mg_ref_base_new (conf));
323
 
        mg_ref_base_set_ref_name (mg_join->priv->target1, MG_TARGET_TYPE, REFERENCE_BY_XML_ID, target_1_xml_id);
324
 
 
325
 
        mg_join->priv->target2 = MG_REF_BASE (mg_ref_base_new (conf));
326
 
        mg_ref_base_set_ref_name (mg_join->priv->target2, MG_TARGET_TYPE, REFERENCE_BY_XML_ID, target_2_xml_id);
327
 
        
328
 
        g_signal_connect (G_OBJECT (query), "nullified",
329
 
                          G_CALLBACK (nullified_object_cb), mg_join);
330
 
 
331
 
        g_signal_connect (G_OBJECT (query), "target_removed",
332
 
                          G_CALLBACK (target_removed_cb), mg_join);
333
 
 
334
 
        /* if the references to the any target is lost then we want to nullify the join */
335
 
        g_signal_connect (G_OBJECT (mg_join->priv->target1), "ref_lost",
336
 
                          G_CALLBACK (target_ref_lost_cb), mg_join);
337
 
        g_signal_connect (G_OBJECT (mg_join->priv->target2), "ref_lost",
338
 
                          G_CALLBACK (target_ref_lost_cb), mg_join);
339
 
 
340
 
        return obj;
341
 
}
342
 
 
343
 
/**
344
 
 * mg_join_new_copy
345
 
 * @orig: a #MgJoin to make a copy of
346
 
 * @replacements: a hash table to store replacements, or %NULL
347
 
 * 
348
 
 * Copy constructor
349
 
 *
350
 
 * Returns: a the new copy of @orig
351
 
 */
352
 
GObject *
353
 
mg_join_new_copy (MgJoin *orig, GHashTable *replacements)
354
 
{
355
 
        GObject   *obj;
356
 
        MgJoin *mg_join;
357
 
        MgConf *conf;
358
 
 
359
 
        g_return_val_if_fail (orig && IS_MG_JOIN (orig), NULL);
360
 
 
361
 
        conf = mg_base_get_conf (MG_BASE (orig));
362
 
        obj = g_object_new (MG_JOIN_TYPE, "conf", conf, NULL);
363
 
        mg_join = MG_JOIN (obj);
364
 
        mg_base_set_id (MG_BASE (mg_join), 0);
365
 
        if (replacements)
366
 
                g_hash_table_insert (replacements, orig, mg_join);
367
 
 
368
 
        mg_join->priv->query = orig->priv->query;
369
 
        g_signal_connect (G_OBJECT (orig->priv->query), "nullified",
370
 
                          G_CALLBACK (nullified_object_cb), mg_join);
371
 
 
372
 
        g_signal_connect (G_OBJECT (orig->priv->query), "target_removed",
373
 
                          G_CALLBACK (target_removed_cb), mg_join);
374
 
 
375
 
        mg_join->priv->target1 = MG_REF_BASE (mg_ref_base_new_copy (orig->priv->target1));
376
 
        mg_join->priv->target2 = MG_REF_BASE (mg_ref_base_new_copy (orig->priv->target2));
377
 
        mg_join->priv->join_type = orig->priv->join_type;
378
 
 
379
 
        /* if the references to the any target is lost then we want to nullify the join */
380
 
        g_signal_connect (G_OBJECT (mg_join->priv->target1), "ref_lost",
381
 
                          G_CALLBACK (target_ref_lost_cb), mg_join);
382
 
        g_signal_connect (G_OBJECT (mg_join->priv->target2), "ref_lost",
383
 
                          G_CALLBACK (target_ref_lost_cb), mg_join);
384
 
        
385
 
        /* join condition */
386
 
        if (orig->priv->cond) {
387
 
                MgCondition *cond = mg_condition_new_copy (orig->priv->cond, replacements);
388
 
                mg_join_set_condition (mg_join, cond);
389
 
                g_object_unref (G_OBJECT (cond));
390
 
                if (replacements)
391
 
                        g_hash_table_insert (replacements, orig->priv->cond, cond);
392
 
        }
393
 
 
394
 
        return obj;     
395
 
}
396
 
 
397
 
static void
398
 
nullified_object_cb (GObject *obj, MgJoin *join)
399
 
{
400
 
        mg_base_nullify (MG_BASE (join));
401
 
}
402
 
 
403
 
static void
404
 
target_ref_lost_cb (MgRefBase *ref, MgJoin *join)
405
 
{
406
 
        mg_base_nullify (MG_BASE (join));
407
 
}
408
 
 
409
 
static void
410
 
target_removed_cb (MgQuery *query, MgTarget *target, MgJoin *join)
411
 
{
412
 
        MgBase *t;
413
 
        gboolean to_nullify = FALSE;
414
 
 
415
 
        t = mg_ref_base_get_ref_object (join->priv->target1);
416
 
        if (t == (MgBase *) target)
417
 
                to_nullify = TRUE;
418
 
        if (! to_nullify) {
419
 
                t = mg_ref_base_get_ref_object (join->priv->target2);
420
 
                if (t == (MgBase *) target)
421
 
                        to_nullify = TRUE;
422
 
        }
423
 
 
424
 
        if (to_nullify)
425
 
                mg_base_nullify (MG_BASE (join));
426
 
}
427
 
 
428
 
static void
429
 
nullified_cond_cb (MgCondition *cond, MgJoin *join) 
430
 
{
431
 
        g_assert (cond == join->priv->cond);
432
 
        g_signal_handlers_disconnect_by_func (G_OBJECT (join->priv->cond),
433
 
                                              G_CALLBACK (nullified_cond_cb), join);
434
 
        g_object_set (G_OBJECT (cond), "join", NULL, NULL);
435
 
        g_object_unref (join->priv->cond);
436
 
        join->priv->cond = NULL;
437
 
}
438
 
 
439
 
static void
440
 
mg_join_dispose (GObject *object)
441
 
{
442
 
        MgJoin *mg_join;
443
 
 
444
 
        g_return_if_fail (object != NULL);
445
 
        g_return_if_fail (IS_MG_JOIN (object));
446
 
 
447
 
        mg_join = MG_JOIN (object);
448
 
        if (mg_join->priv) {
449
 
                mg_base_nullify_check (MG_BASE (object));
450
 
                
451
 
                if (mg_join->priv->query) {
452
 
                        g_signal_handlers_disconnect_by_func (G_OBJECT (mg_join->priv->query),
453
 
                                                              G_CALLBACK (nullified_object_cb), mg_join);
454
 
                        g_signal_handlers_disconnect_by_func (G_OBJECT (mg_join->priv->query),
455
 
                                                              G_CALLBACK (target_removed_cb), mg_join);
456
 
                        mg_join->priv->query = NULL;
457
 
                }
458
 
                if (mg_join->priv->target1) {
459
 
                        g_signal_handlers_disconnect_by_func (G_OBJECT (mg_join->priv->target1),
460
 
                                                              G_CALLBACK (target_ref_lost_cb), mg_join);
461
 
                        g_object_unref (G_OBJECT (mg_join->priv->target1));
462
 
                        mg_join->priv->target1 = NULL;
463
 
                }
464
 
                if (mg_join->priv->target2) {
465
 
                        g_signal_handlers_disconnect_by_func (G_OBJECT (mg_join->priv->target2),
466
 
                                                              G_CALLBACK (target_ref_lost_cb), mg_join);
467
 
                        g_object_unref (G_OBJECT (mg_join->priv->target2));
468
 
                        mg_join->priv->target2 = NULL;
469
 
                }
470
 
                if (mg_join->priv->cond)
471
 
                        nullified_cond_cb (mg_join->priv->cond, mg_join);
472
 
        }
473
 
 
474
 
        /* parent class */
475
 
        parent_class->dispose (object);
476
 
}
477
 
 
478
 
static void
479
 
mg_join_finalize (GObject   * object)
480
 
{
481
 
        MgJoin *mg_join;
482
 
 
483
 
        g_return_if_fail (object != NULL);
484
 
        g_return_if_fail (IS_MG_JOIN (object));
485
 
 
486
 
        mg_join = MG_JOIN (object);
487
 
        if (mg_join->priv) {
488
 
                g_free (mg_join->priv);
489
 
                mg_join->priv = NULL;
490
 
        }
491
 
 
492
 
        /* parent class */
493
 
        parent_class->finalize (object);
494
 
}
495
 
 
496
 
 
497
 
static void 
498
 
mg_join_set_property (GObject              *object,
499
 
                        guint                 param_id,
500
 
                        const GValue         *value,
501
 
                        GParamSpec           *pspec)
502
 
{
503
 
        gpointer ptr;
504
 
        MgJoin *mg_join;
505
 
 
506
 
        mg_join = MG_JOIN (object);
507
 
        if (mg_join->priv) {
508
 
                switch (param_id) {
509
 
                case PROP:
510
 
                        ptr = g_value_get_pointer (value);
511
 
                        break;
512
 
                }
513
 
        }
514
 
}
515
 
 
516
 
static void
517
 
mg_join_get_property (GObject              *object,
518
 
                          guint                 param_id,
519
 
                          GValue               *value,
520
 
                          GParamSpec           *pspec)
521
 
{
522
 
        MgJoin *mg_join;
523
 
        mg_join = MG_JOIN (object);
524
 
        
525
 
        if (mg_join->priv) {
526
 
                switch (param_id) {
527
 
                case PROP:
528
 
                        break;
529
 
                }       
530
 
        }
531
 
}
532
 
 
533
 
/**
534
 
 * mg_join_set_join_type
535
 
 * @join: a #MgJoin object
536
 
 * @type: the new type of join
537
 
 *
538
 
 * Sets the type of @join
539
 
 */
540
 
void
541
 
mg_join_set_join_type (MgJoin *join, MgJoinType type)
542
 
{
543
 
        g_return_if_fail (join && IS_MG_JOIN (join));
544
 
        g_return_if_fail (join->priv);
545
 
        
546
 
        if (join->priv->join_type != type) {
547
 
                join->priv->join_type = type;
548
 
                mg_base_changed (MG_BASE (join));
549
 
        }
550
 
}
551
 
 
552
 
/**
553
 
 * mg_join_get_join_type
554
 
 * @join: a #MgJoin object
555
 
 *
556
 
 * Get the type of a join
557
 
 *
558
 
 * Returns: the type of @join
559
 
 */
560
 
MgJoinType
561
 
mg_join_get_join_type (MgJoin *join)
562
 
{
563
 
        g_return_val_if_fail (join && IS_MG_JOIN (join), MG_JOIN_TYPE_CROSS);
564
 
        g_return_val_if_fail (join->priv, MG_JOIN_TYPE_CROSS);
565
 
        
566
 
        return join->priv->join_type;
567
 
}
568
 
 
569
 
 
570
 
/**
571
 
 * mg_join_get_query
572
 
 * @join: a #MgJoin object
573
 
 * 
574
 
 * Get the #MgQuery to which @join is attached to
575
 
 *
576
 
 * Returns: the #MgQuery
577
 
 */
578
 
MgQuery *
579
 
mg_join_get_query (MgJoin *join)
580
 
{
581
 
        g_return_val_if_fail (join && IS_MG_JOIN (join), NULL);
582
 
        g_return_val_if_fail (join->priv, NULL);
583
 
        
584
 
        return join->priv->query;
585
 
}
586
 
 
587
 
/**
588
 
 * mg_join_get_target_1
589
 
 * @join: a #MgJoin object
590
 
 *
591
 
 * Get the 1st #MgTarget participating in the join
592
 
 *
593
 
 * Returns: the #MgTarget
594
 
 */
595
 
MgTarget *
596
 
mg_join_get_target_1 (MgJoin *join)
597
 
{
598
 
        MgBase *base;
599
 
 
600
 
        g_return_val_if_fail (join && IS_MG_JOIN (join), NULL);
601
 
        g_return_val_if_fail (join->priv, NULL);
602
 
        
603
 
        base = mg_ref_base_get_ref_object (join->priv->target1);
604
 
        if (base)
605
 
                return MG_TARGET (base);
606
 
        else
607
 
                return NULL;
608
 
}
609
 
 
610
 
/**
611
 
 * mg_join_get_target_2
612
 
 * @join: a #MgJoin object
613
 
 *
614
 
 * Get the 2nd #MgTarget participating in the join
615
 
 *
616
 
 * Returns: the #MgTarget
617
 
 */
618
 
MgTarget *
619
 
mg_join_get_target_2 (MgJoin *join)
620
 
{
621
 
        MgBase *base;
622
 
 
623
 
        g_return_val_if_fail (join && IS_MG_JOIN (join), NULL);
624
 
        g_return_val_if_fail (join->priv, NULL);
625
 
        
626
 
        base = mg_ref_base_get_ref_object (join->priv->target2);
627
 
        if (base)
628
 
                return MG_TARGET (base);
629
 
        else
630
 
                return NULL;
631
 
}
632
 
 
633
 
 
634
 
/**
635
 
 * mg_join_swap_targets
636
 
 * @join: a #MgJoin object
637
 
 *
638
 
 * Changes the relative roles of the two #MgTarget objects. It does not
639
 
 * change the join condition itself, and is usefull only for the internals
640
 
 * of the #MgQuery object
641
 
 */
642
 
void
643
 
mg_join_swap_targets (MgJoin *join)
644
 
{
645
 
        MgRefBase *ref;
646
 
        g_return_if_fail (join && IS_MG_JOIN (join));
647
 
        g_return_if_fail (join->priv);
648
 
 
649
 
        ref = join->priv->target1;
650
 
        join->priv->target1 = join->priv->target2;
651
 
        join->priv->target2 = ref;
652
 
 
653
 
        switch (join->priv->join_type) {
654
 
        case MG_JOIN_TYPE_LEFT_OUTER:
655
 
                join->priv->join_type = MG_JOIN_TYPE_RIGHT_OUTER;
656
 
                break;
657
 
        case MG_JOIN_TYPE_RIGHT_OUTER:
658
 
                join->priv->join_type = MG_JOIN_TYPE_LEFT_OUTER;
659
 
                break;
660
 
        default:
661
 
                break;
662
 
        }
663
 
}
664
 
 
665
 
/**
666
 
 * mg_join_set_condition
667
 
 * @join: a #MgJoin object
668
 
 * @cond: a  #MgCondition object, or %NULL to remove the join's condition
669
 
 * 
670
 
 * Sets @cond to be @join's condition. This is possible only if @cond uses
671
 
 * query fields which are either of type MgQfField and reference one of the two
672
 
 * targets which @join uses, or any other query field type.
673
 
 *
674
 
 * Returns: TRUE if no error occurred
675
 
 */
676
 
gboolean
677
 
mg_join_set_condition (MgJoin *join, MgCondition *cond)
678
 
{
679
 
        MgTarget *t1, *t2;
680
 
        g_return_val_if_fail (join && IS_MG_JOIN (join), FALSE);
681
 
        g_return_val_if_fail (join->priv, FALSE);
682
 
 
683
 
        /* test if the condition is OK */
684
 
        if (! mg_condition_represents_join (cond, &t1, &t2, NULL)) 
685
 
                return FALSE;
686
 
 
687
 
        if (! (((mg_ref_base_get_ref_object (join->priv->target1) == (MgBase*) t1) && 
688
 
                (mg_ref_base_get_ref_object (join->priv->target2) == (MgBase*) t2)) ||
689
 
               ((mg_ref_base_get_ref_object (join->priv->target1) == (MgBase*) t2) && 
690
 
                (mg_ref_base_get_ref_object (join->priv->target2) == (MgBase*) t1))))
691
 
                return FALSE;
692
 
 
693
 
        /* actual work */
694
 
        if (join->priv->cond && (join->priv->cond != cond)) 
695
 
                nullified_cond_cb (join->priv->cond, join);
696
 
        
697
 
        if (cond) {
698
 
                g_object_ref (G_OBJECT (cond));
699
 
                g_signal_connect (G_OBJECT (cond), "nullified",
700
 
                                  G_CALLBACK (nullified_cond_cb), join);
701
 
                join->priv->cond = cond;
702
 
                g_object_set (G_OBJECT (cond), "join", join, NULL);
703
 
        }
704
 
 
705
 
        return TRUE;
706
 
}
707
 
 
708
 
/**
709
 
 * mg_join_get_condition
710
 
 * @join: a #MgJoin object
711
 
 *
712
 
 * Get the join's associated condition
713
 
 *
714
 
 * Returns: the #MgCondition object
715
 
 */
716
 
MgCondition *
717
 
mg_join_get_condition (MgJoin *join)
718
 
{
719
 
        g_return_val_if_fail (join && IS_MG_JOIN (join), NULL);
720
 
        g_return_val_if_fail (join->priv, NULL);
721
 
        
722
 
        return join->priv->cond;
723
 
}
724
 
 
725
 
 
726
 
/**
727
 
 * mg_join_render_type
728
 
 * @join: a #MgJoin object
729
 
 *
730
 
 * Get the SQL version of the join type ("INNER JOIN", "LEFT JOIN", etc)
731
 
 *
732
 
 * Returns: the type as a const string
733
 
 */
734
 
const gchar *
735
 
mg_join_render_type (MgJoin *join)
736
 
{
737
 
        g_return_val_if_fail (join && IS_MG_JOIN (join), NULL);
738
 
        g_return_val_if_fail (join->priv, NULL);
739
 
 
740
 
        switch (join->priv->join_type) {
741
 
        case MG_JOIN_TYPE_INNER:
742
 
                return "INNER JOIN";
743
 
        case MG_JOIN_TYPE_LEFT_OUTER:
744
 
                return "LEFT JOIN";
745
 
        case MG_JOIN_TYPE_RIGHT_OUTER:
746
 
                return "RIGHT JOIN";
747
 
        case MG_JOIN_TYPE_FULL_OUTER:
748
 
                return "FULL JOIN";
749
 
        case MG_JOIN_TYPE_CROSS:
750
 
                return "CROSS JOIN";
751
 
        default:
752
 
                g_assert_not_reached ();
753
 
                return NULL;
754
 
        }
755
 
}
756
 
 
757
 
#ifdef debug
758
 
static void
759
 
mg_join_dump (MgJoin *join, guint offset)
760
 
{
761
 
        gchar *str;
762
 
        guint i;
763
 
        
764
 
        g_return_if_fail (join && IS_MG_JOIN (join));
765
 
 
766
 
        /* string for the offset */
767
 
        str = g_new0 (gchar, offset+1);
768
 
        for (i=0; i<offset; i++)
769
 
                str[i] = ' ';
770
 
        str[offset] = 0;
771
 
 
772
 
        /* dump */
773
 
        if (join->priv) {
774
 
                g_print ("%s" D_COL_H1 "MgJoin" D_COL_NOR " %p ", str, join);
775
 
                if (mg_join_is_active (MG_REFERER (join)))
776
 
                        g_print ("Active, Targets: %p -> %p ", mg_ref_base_get_ref_object (join->priv->target1),
777
 
                                 mg_ref_base_get_ref_object (join->priv->target2));
778
 
                else
779
 
                        g_print (D_COL_ERR "Non active" D_COL_NOR ", ");
780
 
                g_print ("requested targets: %s & %s", mg_ref_base_get_ref_name (join->priv->target1, NULL, NULL),
781
 
                         mg_ref_base_get_ref_name (join->priv->target2, NULL, NULL));
782
 
                if (join->priv->cond) {
783
 
                        g_print (", Condition:\n");
784
 
                        mg_base_dump (MG_BASE (join->priv->cond), offset+5);
785
 
                }
786
 
                else
787
 
                        g_print ("\n");
788
 
        }
789
 
        else
790
 
                g_print ("%s" D_COL_ERR "Using finalized object %p" D_COL_NOR, str, join);
791
 
}
792
 
#endif
793
 
 
794
 
 
795
 
 
796
 
/* 
797
 
 * MgReferer interface implementation
798
 
 */
799
 
static gboolean
800
 
mg_join_activate (MgReferer *iface)
801
 
{
802
 
        gboolean retval;
803
 
        g_return_val_if_fail (iface && IS_MG_JOIN (iface), FALSE);
804
 
        g_return_val_if_fail (MG_JOIN (iface)->priv, FALSE);
805
 
 
806
 
        retval = mg_ref_base_activate (MG_JOIN (iface)->priv->target1);
807
 
        retval = mg_ref_base_activate (MG_JOIN (iface)->priv->target2) && retval;
808
 
 
809
 
        return retval;
810
 
}
811
 
 
812
 
static void
813
 
mg_join_deactivate (MgReferer *iface)
814
 
{
815
 
        g_return_if_fail (iface && IS_MG_JOIN (iface));
816
 
        g_return_if_fail (MG_JOIN (iface)->priv);
817
 
 
818
 
        mg_ref_base_deactivate (MG_JOIN (iface)->priv->target1);
819
 
        mg_ref_base_deactivate (MG_JOIN (iface)->priv->target2);
820
 
}
821
 
 
822
 
static gboolean
823
 
mg_join_is_active (MgReferer *iface)
824
 
{
825
 
        gboolean retval;
826
 
 
827
 
        g_return_val_if_fail (iface && IS_MG_JOIN (iface), FALSE);
828
 
        g_return_val_if_fail (MG_JOIN (iface)->priv, FALSE);
829
 
 
830
 
        retval = mg_ref_base_is_active (MG_JOIN (iface)->priv->target1);
831
 
        retval = retval && mg_ref_base_is_active (MG_JOIN (iface)->priv->target2);
832
 
        
833
 
        return retval;
834
 
}
835
 
 
836
 
static GSList *
837
 
mg_join_get_ref_objects (MgReferer *iface)
838
 
{
839
 
        GSList *list = NULL;
840
 
        MgBase *base;
841
 
        g_return_val_if_fail (iface && IS_MG_JOIN (iface), NULL);
842
 
        g_return_val_if_fail (MG_JOIN (iface)->priv, NULL);
843
 
 
844
 
        base = mg_ref_base_get_ref_object (MG_JOIN (iface)->priv->target1);
845
 
        if (base)
846
 
                list = g_slist_append (list, base);
847
 
        base = mg_ref_base_get_ref_object (MG_JOIN (iface)->priv->target2);
848
 
        if (base)
849
 
                list = g_slist_append (list, base);
850
 
 
851
 
        return list;
852
 
}
853
 
 
854
 
static void
855
 
mg_join_replace_refs (MgReferer *iface, GHashTable *replacements)
856
 
{
857
 
        MgJoin *join;
858
 
        g_return_if_fail (iface && IS_MG_JOIN (iface));
859
 
        g_return_if_fail (MG_JOIN (iface)->priv);
860
 
 
861
 
        join = MG_JOIN (iface);
862
 
        if (join->priv->query) {
863
 
                MgQuery *query = g_hash_table_lookup (replacements, join->priv->query);
864
 
                if (query) {
865
 
                        g_signal_handlers_disconnect_by_func (G_OBJECT (join->priv->query),
866
 
                                                              G_CALLBACK (nullified_object_cb), join);
867
 
                        join->priv->query = query;
868
 
                        g_signal_connect (G_OBJECT (query), "nullified",
869
 
                                          G_CALLBACK (nullified_object_cb), join);
870
 
                }
871
 
        }
872
 
 
873
 
        mg_ref_base_replace_ref_object (join->priv->target1, replacements);
874
 
        mg_ref_base_replace_ref_object (join->priv->target2, replacements);
875
 
        if (join->priv->cond) 
876
 
                mg_referer_replace_refs (MG_REFERER (join->priv->cond), replacements);
877
 
}
878
 
 
879
 
/* 
880
 
 * MgXmlStorage interface implementation
881
 
 */
882
 
 
883
 
static const gchar *convert_join_type_to_str (MgJoinType type);
884
 
static MgJoinType   convert_str_to_join_type (const gchar *str);
885
 
 
886
 
static gchar *
887
 
mg_join_get_xml_id (MgXmlStorage *iface)
888
 
{
889
 
        g_return_val_if_fail (iface && IS_MG_JOIN (iface), NULL);
890
 
        g_return_val_if_fail (MG_JOIN (iface)->priv, NULL);
891
 
 
892
 
        return NULL;
893
 
}
894
 
 
895
 
static xmlNodePtr
896
 
mg_join_save_to_xml (MgXmlStorage *iface, GError **error)
897
 
{
898
 
        xmlNodePtr node = NULL;
899
 
        MgJoin *join;
900
 
        gchar *str;
901
 
        const gchar *type;
902
 
 
903
 
        g_return_val_if_fail (iface && IS_MG_JOIN (iface), NULL);
904
 
        g_return_val_if_fail (MG_JOIN (iface)->priv, NULL);
905
 
 
906
 
        join = MG_JOIN (iface);
907
 
 
908
 
        node = xmlNewNode (NULL, "MG_JOIN");
909
 
        
910
 
        /* targets */
911
 
        if (join->priv->target1) {
912
 
                str = NULL;
913
 
                if (mg_ref_base_is_active (join->priv->target1)) {
914
 
                        MgBase *base = mg_ref_base_get_ref_object (join->priv->target1);
915
 
                        g_assert (base);
916
 
                        str = mg_xml_storage_get_xml_id (MG_XML_STORAGE (base));
917
 
                }
918
 
                else 
919
 
                        str = g_strdup (mg_ref_base_get_ref_name (join->priv->target1, NULL, NULL));
920
 
                
921
 
                if (str) {
922
 
                        xmlSetProp (node, "target1", str);
923
 
                        g_free (str);
924
 
                }
925
 
        }
926
 
 
927
 
        if (join->priv->target2) {
928
 
                str = NULL;
929
 
                if (mg_ref_base_is_active (join->priv->target2)) {
930
 
                        MgBase *base = mg_ref_base_get_ref_object (join->priv->target2);
931
 
                        g_assert (base);
932
 
                        str = mg_xml_storage_get_xml_id (MG_XML_STORAGE (base));
933
 
                }
934
 
                else 
935
 
                        str = g_strdup (mg_ref_base_get_ref_name (join->priv->target2, NULL, NULL));
936
 
                
937
 
                if (str) {
938
 
                        xmlSetProp (node, "target2", str);
939
 
                        g_free (str);
940
 
                }
941
 
        }
942
 
 
943
 
        /* join type */
944
 
        type = convert_join_type_to_str (join->priv->join_type);
945
 
        xmlSetProp (node, "join_type", type);
946
 
 
947
 
        /* join condition */
948
 
        if (join->priv->cond) {
949
 
                xmlNodePtr sub = mg_xml_storage_save_to_xml (MG_XML_STORAGE (join->priv->cond), error);
950
 
                if (sub)
951
 
                        xmlAddChild (node, sub);
952
 
                else {
953
 
                        xmlFreeNode (node);
954
 
                        return NULL;
955
 
                }
956
 
        }
957
 
 
958
 
        return node;
959
 
}
960
 
 
961
 
 
962
 
static gboolean
963
 
mg_join_load_from_xml (MgXmlStorage *iface, xmlNodePtr node, GError **error)
964
 
{
965
 
        MgJoin *join;
966
 
        gchar *prop;
967
 
        gboolean t1 = FALSE, t2 = FALSE;
968
 
        xmlNodePtr children;
969
 
 
970
 
        g_return_val_if_fail (iface && IS_MG_JOIN (iface), FALSE);
971
 
        g_return_val_if_fail (MG_JOIN (iface)->priv, FALSE);
972
 
        g_return_val_if_fail (node, FALSE);
973
 
 
974
 
        join = MG_JOIN (iface);
975
 
        if (strcmp (node->name, "MG_JOIN")) {
976
 
                g_set_error (error,
977
 
                             MG_JOIN_ERROR,
978
 
                             MG_JOIN_XML_LOAD_ERROR,
979
 
                             _("XML Tag is not <MG_JOIN>"));
980
 
                return FALSE;
981
 
        }
982
 
 
983
 
        prop = xmlGetProp (node, "target1");
984
 
        if (prop) {
985
 
                if (join->priv->target1) {
986
 
                        mg_ref_base_set_ref_name (join->priv->target1, MG_TARGET_TYPE, 
987
 
                                                  REFERENCE_BY_XML_ID, prop);
988
 
                        t1 = TRUE;
989
 
                }
990
 
                g_free (prop);
991
 
        }
992
 
        prop = xmlGetProp (node, "target2");
993
 
        if (prop) {
994
 
                if (join->priv->target2) {
995
 
                        mg_ref_base_set_ref_name (join->priv->target2, MG_TARGET_TYPE, 
996
 
                                                  REFERENCE_BY_XML_ID, prop);
997
 
                        t2 = TRUE;
998
 
                }
999
 
                g_free (prop);
1000
 
        }
1001
 
        prop = xmlGetProp (node, "join_type");
1002
 
        if (prop) {
1003
 
                join->priv->join_type = convert_str_to_join_type (prop);
1004
 
                g_free (prop);
1005
 
        }
1006
 
 
1007
 
        /* children nodes */
1008
 
        children = node->children;
1009
 
        while (children) {
1010
 
                if (!strcmp (children->name, "MG_COND")) {
1011
 
                        MgCondition *cond;
1012
 
 
1013
 
                        cond = MG_CONDITION (mg_condition_new (join->priv->query, MG_CONDITION_NODE_AND));
1014
 
                        if (mg_xml_storage_load_from_xml (MG_XML_STORAGE (cond), children, error)) {
1015
 
                                mg_join_set_condition (join, cond);
1016
 
                                g_object_unref (G_OBJECT (cond));
1017
 
                        }
1018
 
                        else
1019
 
                                return FALSE;
1020
 
                }
1021
 
 
1022
 
                children = children->next;
1023
 
        }
1024
 
        if (t1 && t2)
1025
 
                return TRUE;
1026
 
        else {
1027
 
                g_set_error (error,
1028
 
                             MG_JOIN_ERROR,
1029
 
                             MG_JOIN_XML_LOAD_ERROR,
1030
 
                             _("Problem loading <MG_JOIN>"));
1031
 
                return FALSE;
1032
 
        }
1033
 
}
1034
 
 
1035
 
static const gchar *
1036
 
convert_join_type_to_str (MgJoinType type)
1037
 
{
1038
 
        switch (type) {
1039
 
        default:
1040
 
        case MG_JOIN_TYPE_INNER:
1041
 
                return "INNER";
1042
 
        case MG_JOIN_TYPE_LEFT_OUTER:
1043
 
                return "LEFT";
1044
 
        case MG_JOIN_TYPE_RIGHT_OUTER:
1045
 
                return "RIGHT";
1046
 
        case MG_JOIN_TYPE_FULL_OUTER:
1047
 
                return "FULL";
1048
 
        case MG_JOIN_TYPE_CROSS:
1049
 
                return "CROSS";
1050
 
        }
1051
 
}
1052
 
 
1053
 
static MgJoinType
1054
 
convert_str_to_join_type (const gchar *str)
1055
 
{
1056
 
        switch (*str) {
1057
 
        case 'I':
1058
 
        default:
1059
 
                return MG_JOIN_TYPE_INNER;
1060
 
        case 'L':
1061
 
                return MG_JOIN_TYPE_LEFT_OUTER;
1062
 
        case 'R':
1063
 
                return MG_JOIN_TYPE_RIGHT_OUTER;
1064
 
        case 'F':
1065
 
                return MG_JOIN_TYPE_FULL_OUTER;
1066
 
        case 'C':
1067
 
                return MG_JOIN_TYPE_CROSS;
1068
 
        }
1069
 
}