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