]> git.sur5r.net Git - glabels/blob - src/merge.c
f7db354fee43ed4584b7678a7bcbbb06dd929c03
[glabels] / src / merge.c
1 /*
2  *  merge.c
3  *  Copyright (C) 2001-2009  Jim Evins <evins@snaught.com>.
4  *
5  *  This file is part of gLabels.
6  *
7  *  gLabels is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  gLabels is distributed in the hope that it will be useful,
13  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
14  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  *  GNU General Public License for more details.
16  *
17  *  You should have received a copy of the GNU General Public License
18  *  along with gLabels.  If not, see <http://www.gnu.org/licenses/>.
19  */
20
21 #include <config.h>
22
23 #include "merge.h"
24
25 #include <glib/gi18n.h>
26 #include <gobject/gvaluecollector.h>
27 #include <string.h>
28
29 #include <libglabels/libglabels.h>
30
31 #include "debug.h"
32
33 /*========================================================*/
34 /* Private types.                                         */
35 /*========================================================*/
36
37 struct _glMergePrivate {
38         gchar             *name;
39         gchar             *description;
40         gchar             *src;
41         glMergeSrcType     src_type;
42
43         GList             *record_list;
44 };
45
46 enum {
47         LAST_SIGNAL
48 };
49
50 typedef struct {
51
52         GType              type;
53         gchar             *name;
54         gchar             *description;
55         glMergeSrcType     src_type;
56
57         guint              n_params;
58         GParameter        *params;
59
60 } Backend;
61
62 /*========================================================*/
63 /* Private globals.                                       */
64 /*========================================================*/
65
66 static GList *backends = NULL;
67
68 /*========================================================*/
69 /* Private function prototypes.                           */
70 /*========================================================*/
71
72 static void           gl_merge_finalize      (GObject        *object);
73
74 static void           merge_open             (glMerge        *merge);
75
76 static void           merge_close            (glMerge        *merge);
77
78 static glMergeRecord *merge_get_record       (glMerge        *merge);
79
80 static void           merge_free_record      (glMergeRecord **record);
81
82 static glMergeRecord *merge_dup_record       (glMergeRecord  *record);
83
84 static void           merge_free_record_list (GList         **record_list);
85
86 static GList         *merge_dup_record_list  (GList          *record_list);
87
88
89
90 \f
91 /*****************************************************************************/
92 /* Register a new merge backend.                                             */
93 /*****************************************************************************/
94 void
95 gl_merge_register_backend (GType              type,
96                            gchar             *name,
97                            gchar             *description,
98                            glMergeSrcType     src_type,
99                            const gchar       *first_arg_name,
100                            ...)
101 {
102         Backend      *backend;
103         va_list       args;
104         const gchar  *pname;
105         GObjectClass *class;
106         GParamSpec   *pspec;
107         GParameter   *params;
108         guint         n_params = 0, n_alloced_params = 16;
109
110         backend = g_new0 (Backend, 1);
111
112         backend->type        = type;
113         backend->name        = g_strdup (name);
114         backend->description = g_strdup (description);
115         backend->src_type    = src_type;
116
117         params = g_new (GParameter, n_alloced_params);
118         va_start (args, first_arg_name);
119         for ( pname=first_arg_name; pname != NULL; pname=va_arg (args,gchar *) ) {
120                 gchar *error = NULL;
121
122                 class = g_type_class_ref (type);
123                 if (class == NULL) {
124                         g_message ("%s: unknown object type %d",
125                                    G_STRLOC, (int)type);
126                         break;
127                 }
128                 pspec = g_object_class_find_property (class, pname);
129                 if (pspec == NULL) {
130                         g_message ("%s: object class `%s' has no property named `%s'",
131                                    G_STRLOC, g_type_name (type), pname);
132                         break;
133                 }
134                 if (n_params >= n_alloced_params) {
135                         n_alloced_params += 16;
136                         params = g_renew (GParameter, params, n_alloced_params);
137                 }
138                 params[n_params].name = pname;
139                 params[n_params].value.g_type = 0;
140                 g_value_init (&params[n_params].value, pspec->value_type);
141                 G_VALUE_COLLECT (&params[n_params].value, args, 0, &error);
142                 if (error) {
143                         g_message ("%s: %s", G_STRLOC, error);
144                         g_free (error);
145                         break;
146                 }
147
148                 n_params++;
149         }
150         va_end (args);
151
152         backend->n_params = n_params;
153         backend->params   = params;
154
155         backends = g_list_append (backends, backend);
156
157 }
158
159 /*****************************************************************************/
160 /* Get list of registered backend descriptions.                              */
161 /*****************************************************************************/
162 GList *
163 gl_merge_get_descriptions (void)
164 {
165         GList   *descriptions = NULL;
166         GList   *p;
167         Backend *backend;
168
169         descriptions = g_list_append (descriptions, g_strdup(_("None")));
170
171         for ( p=backends; p!=NULL; p=p->next) {
172                 backend = (Backend *)p->data;
173                 descriptions = g_list_append (descriptions,
174                                               g_strdup(backend->description));
175         }
176
177         return descriptions;
178 }
179
180 /*****************************************************************************/
181 /* Free list of descriptions.                                                */
182 /*****************************************************************************/
183 void
184 gl_merge_free_descriptions (GList **descriptions)
185 {
186         GList *p;
187
188         gl_debug (DEBUG_MERGE, "START");
189
190         for (p = *descriptions; p != NULL; p = p->next) {
191                 g_free (p->data);
192                 p->data = NULL;
193         }
194
195         g_list_free (*descriptions);
196         *descriptions = NULL;
197
198         gl_debug (DEBUG_MERGE, "END");
199 }
200
201 /*****************************************************************************/
202 /* Lookup name of backend from description.                                  */
203 /*****************************************************************************/
204 gchar *
205 gl_merge_description_to_name (gchar *description)
206 {
207         GList   *p;
208         Backend *backend;
209
210         if (lgl_str_utf8_casecmp(description, _("None")) == 0) {
211                 return g_strdup("None");
212         }
213
214         for ( p=backends; p!=NULL; p=p->next) {
215                 backend = (Backend *)p->data;
216                 if (lgl_str_utf8_casecmp(description, backend->description) == 0) {
217                         return g_strdup(backend->name);
218                 }
219         }
220
221         return g_strdup("None");
222 }
223
224 /*****************************************************************************/
225 /* Boilerplate object stuff.                                                 */
226 /*****************************************************************************/
227 G_DEFINE_TYPE (glMerge, gl_merge, G_TYPE_OBJECT);
228
229 static void
230 gl_merge_class_init (glMergeClass *class)
231 {
232         GObjectClass *object_class = G_OBJECT_CLASS (class);
233
234         gl_debug (DEBUG_MERGE, "START");
235
236         gl_merge_parent_class = g_type_class_peek_parent (class);
237
238         object_class->finalize = gl_merge_finalize;
239
240         gl_debug (DEBUG_MERGE, "END");
241 }
242
243 static void
244 gl_merge_init (glMerge *merge)
245 {
246         gl_debug (DEBUG_MERGE, "START");
247
248         merge->priv = g_new0 (glMergePrivate, 1);
249
250         gl_debug (DEBUG_MERGE, "END");
251 }
252
253 static void
254 gl_merge_finalize (GObject *object)
255 {
256         glMerge *merge = GL_MERGE (object);
257
258         gl_debug (DEBUG_MERGE, "START");
259
260         g_return_if_fail (object && GL_IS_MERGE (object));
261
262         merge_free_record_list (&merge->priv->record_list);
263         g_free (merge->priv->name);
264         g_free (merge->priv->description);
265         g_free (merge->priv->src);
266         g_free (merge->priv);
267
268         G_OBJECT_CLASS (gl_merge_parent_class)->finalize (object);
269
270         gl_debug (DEBUG_MERGE, "END");
271 }
272
273 /*****************************************************************************/
274 /* New merge object.                                                         */
275 /*****************************************************************************/
276 glMerge *
277 gl_merge_new (gchar *name)
278 {
279         glMerge *merge = NULL;
280         GList   *p;
281         Backend *backend;
282
283         gl_debug (DEBUG_MERGE, "START");
284
285         for (p=backends; p!=NULL; p=p->next) {
286                 backend = (Backend *)p->data;
287
288                 if (g_strcasecmp(name, backend->name) == 0) {
289
290                         merge = GL_MERGE (g_object_newv (backend->type,
291                                                          backend->n_params,
292                                                          backend->params));
293
294                         merge->priv->name        = g_strdup (name);
295                         merge->priv->description = g_strdup (backend->description);
296                         merge->priv->src_type    = backend->src_type;
297
298                         break;
299                 }
300         }
301
302         if ( (merge == NULL) && (g_strcasecmp (name, "None") != 0)) {
303                 g_message ("Unknown merge backend \"%s\"", name);
304         }
305
306         gl_debug (DEBUG_MERGE, "END");
307
308         return merge;
309 }
310
311 /*****************************************************************************/
312 /* Duplicate merge.                                                         */
313 /*****************************************************************************/
314 glMerge *
315 gl_merge_dup (glMerge *src_merge)
316 {
317         glMerge    *dst_merge;
318
319         gl_debug (DEBUG_MERGE, "START");
320
321         if (src_merge == NULL) {
322                 gl_debug (DEBUG_MERGE, "END (NULL)");
323                 return NULL;
324         }
325
326         g_return_val_if_fail (GL_IS_MERGE (src_merge), NULL);
327
328         dst_merge = g_object_new (G_OBJECT_TYPE(src_merge), NULL);
329         dst_merge->priv->name        = g_strdup (src_merge->priv->name);
330         dst_merge->priv->description = g_strdup (src_merge->priv->description);
331         dst_merge->priv->src         = g_strdup (src_merge->priv->src);
332         dst_merge->priv->src_type    = src_merge->priv->src_type;
333         dst_merge->priv->record_list 
334                 = merge_dup_record_list (src_merge->priv->record_list);
335
336         if ( GL_MERGE_GET_CLASS(src_merge)->copy != NULL ) {
337
338                 /* We have an object specific method, use it */
339                 GL_MERGE_GET_CLASS(src_merge)->copy (dst_merge, src_merge);
340
341         }
342
343         gl_debug (DEBUG_MERGE, "END");
344
345         return dst_merge;
346 }
347
348 /*****************************************************************************/
349 /* Get name of merge.                                                        */
350 /*****************************************************************************/
351 gchar *
352 gl_merge_get_name (glMerge *merge)
353 {
354         gl_debug (DEBUG_MERGE, "");
355
356         if (merge == NULL) {
357                 return g_strdup("None");
358         }
359
360         g_return_val_if_fail (GL_IS_MERGE (merge), g_strdup("None"));
361
362         return g_strdup(merge->priv->name);
363 }
364
365 /*****************************************************************************/
366 /* Get description of merge.                                                 */
367 /*****************************************************************************/
368 gchar *
369 gl_merge_get_description (glMerge *merge)
370 {
371         gl_debug (DEBUG_MERGE, "");
372
373         if (merge == NULL) {
374                 return g_strdup(_("None"));
375         }
376
377         g_return_val_if_fail (GL_IS_MERGE (merge), g_strdup(_("None")));
378
379         return g_strdup(merge->priv->description);
380 }
381
382 /*****************************************************************************/
383 /* Get source type of merge.                                                 */
384 /*****************************************************************************/
385 glMergeSrcType
386 gl_merge_get_src_type (glMerge *merge)
387 {
388         gl_debug (DEBUG_MERGE, "");
389
390         if (merge == NULL) {
391                 return GL_MERGE_SRC_IS_FIXED;
392         }
393
394         g_return_val_if_fail (GL_IS_MERGE (merge), GL_MERGE_SRC_IS_FIXED);
395
396         return merge->priv->src_type;
397 }
398
399 /*****************************************************************************/
400 /* Set src of merge.                                                         */
401 /*****************************************************************************/
402 void
403 gl_merge_set_src (glMerge *merge,
404                   gchar   *src)
405 {
406         GList         *record_list = NULL;
407         glMergeRecord *record;
408
409         gl_debug (DEBUG_MERGE, "START");
410
411         if (merge == NULL)
412         {
413                 gl_debug (DEBUG_MERGE, "END (NULL)");
414                 return;
415         }
416
417         g_return_if_fail (GL_IS_MERGE (merge));
418
419         if ( src == NULL)
420         {
421
422                 if ( merge->priv->src != NULL )
423                 {
424                         g_free (merge->priv->src);
425                 }
426                 merge->priv->src = NULL;
427                 merge_free_record_list (&merge->priv->record_list);
428
429         }
430         else
431         {
432
433                 if ( merge->priv->src != NULL )
434                 {
435                         g_free(merge->priv->src);
436                 }
437                 merge->priv->src = g_strdup (src);
438
439                 merge_free_record_list (&merge->priv->record_list);
440                         
441                 merge_open (merge);
442                 while ( (record = merge_get_record (merge)) != NULL )
443                 {
444                         record_list = g_list_append( record_list, record );
445                 }
446                 merge_close (merge);
447                 merge->priv->record_list = record_list;
448
449         }
450                      
451
452         gl_debug (DEBUG_MERGE, "END");
453 }
454
455 /*****************************************************************************/
456 /* Get src of merge.                                                         */
457 /*****************************************************************************/
458 gchar *
459 gl_merge_get_src (glMerge *merge)
460 {
461         gl_debug (DEBUG_MERGE, "");
462
463         if (merge == NULL) {
464                 return NULL;
465         }
466
467         g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
468
469         return g_strdup(merge->priv->src);
470 }
471
472 /*****************************************************************************/
473 /* Get Key List.                                                             */
474 /*****************************************************************************/
475 GList *
476 gl_merge_get_key_list (glMerge *merge)
477 {
478         GList *key_list = NULL;
479
480         gl_debug (DEBUG_MERGE, "START");
481
482         if (merge == NULL) {
483                 return NULL;
484         }
485
486         g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
487
488         if ( GL_MERGE_GET_CLASS(merge)->get_key_list != NULL ) {
489
490                 key_list = GL_MERGE_GET_CLASS(merge)->get_key_list (merge);
491
492         }
493
494         gl_debug (DEBUG_MERGE, "END");
495
496         return key_list;
497 }
498
499 /*****************************************************************************/
500 /* Free a list of keys.                                                      */
501 /*****************************************************************************/
502 void
503 gl_merge_free_key_list (GList **key_list)
504 {
505         GList *p;
506
507         gl_debug (DEBUG_MERGE, "START");
508
509         for (p = *key_list; p != NULL; p = p->next) {
510                 g_free (p->data);
511                 p->data = NULL;
512         }
513
514         g_list_free (*key_list);
515         *key_list = NULL;
516
517         gl_debug (DEBUG_MERGE, "END");
518 }
519
520 /*****************************************************************************/
521 /* Get Key List.                                                             */
522 /*****************************************************************************/
523 gchar *
524 gl_merge_get_primary_key (glMerge *merge)
525 {
526         gchar *key = NULL;
527
528         gl_debug (DEBUG_MERGE, "START");
529
530         if (merge == NULL) {
531                 return NULL;
532         }
533
534         g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
535
536         if ( GL_MERGE_GET_CLASS(merge)->get_primary_key != NULL ) {
537
538                 key = GL_MERGE_GET_CLASS(merge)->get_primary_key (merge);
539
540         }
541
542         gl_debug (DEBUG_MERGE, "END");
543
544         return key;
545 }
546
547 /*---------------------------------------------------------------------------*/
548 /* Open merge source.                                                        */
549 /*---------------------------------------------------------------------------*/
550 static void
551 merge_open (glMerge *merge)
552 {
553         gl_debug (DEBUG_MERGE, "START");
554
555         g_return_if_fail (merge && GL_IS_MERGE (merge));
556
557         if ( GL_MERGE_GET_CLASS(merge)->open != NULL ) {
558
559                 GL_MERGE_GET_CLASS(merge)->open (merge);
560
561         }
562
563         gl_debug (DEBUG_MERGE, "END");
564 }
565
566 /*---------------------------------------------------------------------------*/
567 /* Close merge source.                                                       */
568 /*---------------------------------------------------------------------------*/
569 static void
570 merge_close (glMerge *merge)
571 {
572         gl_debug (DEBUG_MERGE, "START");
573
574         g_return_if_fail (merge && GL_IS_MERGE (merge));
575
576         if ( GL_MERGE_GET_CLASS(merge)->close != NULL ) {
577
578                 GL_MERGE_GET_CLASS(merge)->close (merge);
579
580         }
581
582         gl_debug (DEBUG_MERGE, "END");
583 }
584
585 /*---------------------------------------------------------------------------*/
586 /* Get next record (list of fields) from opened merge source.                */
587 /*---------------------------------------------------------------------------*/
588 static glMergeRecord *
589 merge_get_record (glMerge *merge)
590 {
591         glMergeRecord *record = NULL;
592
593         gl_debug (DEBUG_MERGE, "START");
594
595         g_return_val_if_fail (merge && GL_IS_MERGE (merge), NULL);
596
597         if ( GL_MERGE_GET_CLASS(merge)->get_record != NULL ) {
598
599                 record = GL_MERGE_GET_CLASS(merge)->get_record (merge);
600
601         }
602
603         gl_debug (DEBUG_MERGE, "END");
604
605         return record;
606 }
607
608 /*---------------------------------------------------------------------------*/
609 /* Free a merge record (list of fields)                                      */
610 /*---------------------------------------------------------------------------*/
611 static void
612 merge_free_record (glMergeRecord **record)
613 {
614         GList *p;
615         glMergeField *field;
616
617         gl_debug (DEBUG_MERGE, "START");
618
619         for (p = (*record)->field_list; p != NULL; p = p->next) {
620                 field = (glMergeField *) p->data;
621
622                 g_free (field->key);
623                 field->key = NULL;
624                 g_free (field->value);
625                 field->value = NULL;
626
627                 g_free (p->data);
628                 p->data = NULL;
629
630         }
631         g_list_free ((*record)->field_list);
632         (*record)->field_list = NULL;
633
634         g_free (*record);
635         *record = NULL;
636
637         gl_debug (DEBUG_MERGE, "END");
638 }
639
640 /*---------------------------------------------------------------------------*/
641 /* Duplicate a merge record (list of fields)                                 */
642 /*---------------------------------------------------------------------------*/
643 static glMergeRecord *
644 merge_dup_record (glMergeRecord *record)
645 {
646         glMergeRecord *dest_record;
647         GList         *p;
648         glMergeField  *dest_field, *field;
649
650         gl_debug (DEBUG_MERGE, "START");
651
652         dest_record = g_new0 (glMergeRecord, 1);
653         dest_record->select_flag = record->select_flag;
654
655         for (p = record->field_list; p != NULL; p = p->next) {
656                 field = (glMergeField *) p->data;
657
658                 dest_field = g_new0 (glMergeField, 1);
659
660                 dest_field->key   = g_strdup (field->key);
661                 dest_field->value = g_strdup (field->value);
662
663                 dest_record->field_list =
664                         g_list_append (dest_record->field_list, dest_field);
665
666         }
667
668         gl_debug (DEBUG_MERGE, "END");
669
670         return dest_record;
671 }
672
673 /*****************************************************************************/
674 /* Find key in given record and evaluate.                                    */
675 /*****************************************************************************/
676 gchar *
677 gl_merge_eval_key (glMergeRecord *record,
678                    gchar         *key)
679                    
680 {
681         GList        *p;
682         glMergeField *field;
683         gchar        *val = NULL;
684
685         gl_debug (DEBUG_MERGE, "START");
686
687         if ( (record != NULL) && (key != NULL) ) {
688                 for (p = record->field_list; p != NULL; p = p->next) {
689                         field = (glMergeField *) p->data;
690
691                         if (strcmp (key, field->key) == 0) {
692                                 val = g_strdup (field->value);
693                         }
694
695                 }
696         }
697
698         gl_debug (DEBUG_MERGE, "END");
699
700         return val;
701 }
702
703 /*****************************************************************************/
704 /* Read all records from merge source.                                       */
705 /*****************************************************************************/
706 const GList *
707 gl_merge_get_record_list (glMerge *merge)
708 {
709         gl_debug (DEBUG_MERGE, "");
710               
711         if ( merge != NULL ) {
712                 return merge->priv->record_list;
713         } else {
714                 return NULL;
715         }
716 }
717
718 /*---------------------------------------------------------------------------*/
719 /* Free a list of records.                                                   */
720 /*---------------------------------------------------------------------------*/
721 static void
722 merge_free_record_list (GList **record_list)
723 {
724         GList *p;
725         glMergeRecord *record;
726
727         gl_debug (DEBUG_MERGE, "START");
728
729         for (p = *record_list; p != NULL; p = p->next) {
730                 record = (glMergeRecord *) p->data;
731
732                 merge_free_record( &record );
733
734         }
735
736         g_list_free (*record_list);
737         *record_list = NULL;
738
739         gl_debug (DEBUG_MERGE, "END");
740 }
741
742 /*---------------------------------------------------------------------------*/
743 /* Duplicate a list of records.                                              */
744 /*---------------------------------------------------------------------------*/
745 static GList *
746 merge_dup_record_list (GList *record_list)
747 {
748         GList         *dest_list = NULL, *p;
749         glMergeRecord *dest_record, *record;
750
751         gl_debug (DEBUG_MERGE, "START");
752
753         for (p = record_list; p != NULL; p = p->next) {
754                 record = (glMergeRecord *) p->data;
755
756                 dest_record = merge_dup_record( record );
757                 dest_list = g_list_append (dest_list, dest_record);
758         }
759
760
761         gl_debug (DEBUG_MERGE, "END");
762
763         return dest_list;
764 }
765
766 /*****************************************************************************/
767 /* Count selected records.                                                   */
768 /*****************************************************************************/
769 gint
770 gl_merge_get_record_count (glMerge *merge)
771 {
772         GList *p;
773         glMergeRecord *record;
774         gint count;
775
776         gl_debug (DEBUG_MERGE, "START");
777
778         count = 0;
779         for ( p=merge->priv->record_list; p!=NULL; p=p->next ) {
780                 record = (glMergeRecord *)p->data;
781
782                 if ( record->select_flag ) count ++;
783         }
784
785         gl_debug (DEBUG_MERGE, "END");
786
787         return count;
788 }
789
790
791
792 /*
793  * Local Variables:       -- emacs
794  * mode: C                -- emacs
795  * c-basic-offset: 8      -- emacs
796  * tab-width: 8           -- emacs
797  * indent-tabs-mode: nil  -- emacs
798  * End:                   -- emacs
799  */