]> git.sur5r.net Git - glabels/blob - glabels2/src/merge.c
2009-09-01 Jim Evins <evins@snaught.com>
[glabels] / glabels2 / src / merge.c
1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 8 -*- */
2
3 /*
4  *  (GLABELS) Label and Business Card Creation program for GNOME
5  *
6  *  merge.c:  document merge module
7  *
8  *  Copyright (C) 2001-2002  Jim Evins <evins@snaught.com>.
9  *
10  *  This program is free software; you can redistribute it and/or modify
11  *  it under the terms of the GNU General Public License as published by
12  *  the Free Software Foundation; either version 2 of the License, or
13  *  (at your option) any later version.
14  *
15  *  This program is distributed in the hope that it will be useful,
16  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
17  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  *  GNU General Public License for more details.
19  *
20  *  You should have received a copy of the GNU General Public License
21  *  along with this program; if not, write to the Free Software
22  *  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
23  */
24 #include <config.h>
25
26 #include "merge.h"
27
28 #include <glib/gi18n.h>
29 #include <gobject/gvaluecollector.h>
30 #include <string.h>
31
32 #include <libglabels/str.h>
33
34 #include "debug.h"
35
36 /*========================================================*/
37 /* Private types.                                         */
38 /*========================================================*/
39
40 struct _glMergePrivate {
41         gchar             *name;
42         gchar             *description;
43         gchar             *src;
44         glMergeSrcType     src_type;
45
46         GList             *record_list;
47 };
48
49 enum {
50         LAST_SIGNAL
51 };
52
53 typedef struct {
54
55         GType              type;
56         gchar             *name;
57         gchar             *description;
58         glMergeSrcType     src_type;
59
60         guint              n_params;
61         GParameter        *params;
62
63 } Backend;
64
65 /*========================================================*/
66 /* Private globals.                                       */
67 /*========================================================*/
68
69 static GList *backends = NULL;
70
71 /*========================================================*/
72 /* Private function prototypes.                           */
73 /*========================================================*/
74
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, (int)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 (lgl_str_utf8_casecmp(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 (lgl_str_utf8_casecmp(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 G_DEFINE_TYPE (glMerge, gl_merge, G_TYPE_OBJECT);
231
232 static void
233 gl_merge_class_init (glMergeClass *class)
234 {
235         GObjectClass *object_class = G_OBJECT_CLASS (class);
236
237         gl_debug (DEBUG_MERGE, "START");
238
239         gl_merge_parent_class = g_type_class_peek_parent (class);
240
241         object_class->finalize = gl_merge_finalize;
242
243         gl_debug (DEBUG_MERGE, "END");
244 }
245
246 static void
247 gl_merge_init (glMerge *merge)
248 {
249         gl_debug (DEBUG_MERGE, "START");
250
251         merge->priv = g_new0 (glMergePrivate, 1);
252
253         gl_debug (DEBUG_MERGE, "END");
254 }
255
256 static void
257 gl_merge_finalize (GObject *object)
258 {
259         glMerge *merge = GL_MERGE (object);
260
261         gl_debug (DEBUG_MERGE, "START");
262
263         g_return_if_fail (object && GL_IS_MERGE (object));
264
265         merge_free_record_list (&merge->priv->record_list);
266         g_free (merge->priv->name);
267         g_free (merge->priv->description);
268         g_free (merge->priv->src);
269         g_free (merge->priv);
270
271         G_OBJECT_CLASS (gl_merge_parent_class)->finalize (object);
272
273         gl_debug (DEBUG_MERGE, "END");
274 }
275
276 /*****************************************************************************/
277 /* New merge object.                                                         */
278 /*****************************************************************************/
279 glMerge *
280 gl_merge_new (gchar *name)
281 {
282         glMerge *merge = NULL;
283         GList   *p;
284         Backend *backend;
285
286         gl_debug (DEBUG_MERGE, "START");
287
288         for (p=backends; p!=NULL; p=p->next) {
289                 backend = (Backend *)p->data;
290
291                 if (g_strcasecmp(name, backend->name) == 0) {
292
293                         merge = GL_MERGE (g_object_newv (backend->type,
294                                                          backend->n_params,
295                                                          backend->params));
296
297                         merge->priv->name        = g_strdup (name);
298                         merge->priv->description = g_strdup (backend->description);
299                         merge->priv->src_type    = backend->src_type;
300
301                         break;
302                 }
303         }
304
305         if ( (merge == NULL) && (g_strcasecmp (name, "None") != 0)) {
306                 g_message ("Unknown merge backend \"%s\"", name);
307         }
308
309         gl_debug (DEBUG_MERGE, "END");
310
311         return merge;
312 }
313
314 /*****************************************************************************/
315 /* Duplicate merge.                                                         */
316 /*****************************************************************************/
317 glMerge *
318 gl_merge_dup (glMerge *src_merge)
319 {
320         glMerge    *dst_merge;
321
322         gl_debug (DEBUG_MERGE, "START");
323
324         if (src_merge == NULL) {
325                 gl_debug (DEBUG_MERGE, "END (NULL)");
326                 return NULL;
327         }
328
329         g_return_val_if_fail (GL_IS_MERGE (src_merge), NULL);
330
331         dst_merge = g_object_new (G_OBJECT_TYPE(src_merge), NULL);
332         dst_merge->priv->name        = g_strdup (src_merge->priv->name);
333         dst_merge->priv->description = g_strdup (src_merge->priv->description);
334         dst_merge->priv->src         = g_strdup (src_merge->priv->src);
335         dst_merge->priv->src_type    = src_merge->priv->src_type;
336         dst_merge->priv->record_list 
337                 = merge_dup_record_list (src_merge->priv->record_list);
338
339         if ( GL_MERGE_GET_CLASS(src_merge)->copy != NULL ) {
340
341                 /* We have an object specific method, use it */
342                 GL_MERGE_GET_CLASS(src_merge)->copy (dst_merge, src_merge);
343
344         }
345
346         gl_debug (DEBUG_MERGE, "END");
347
348         return dst_merge;
349 }
350
351 /*****************************************************************************/
352 /* Get name of merge.                                                        */
353 /*****************************************************************************/
354 gchar *
355 gl_merge_get_name (glMerge *merge)
356 {
357         gl_debug (DEBUG_MERGE, "");
358
359         if (merge == NULL) {
360                 return g_strdup("None");
361         }
362
363         g_return_val_if_fail (GL_IS_MERGE (merge), g_strdup("None"));
364
365         return g_strdup(merge->priv->name);
366 }
367
368 /*****************************************************************************/
369 /* Get description of merge.                                                 */
370 /*****************************************************************************/
371 gchar *
372 gl_merge_get_description (glMerge *merge)
373 {
374         gl_debug (DEBUG_MERGE, "");
375
376         if (merge == NULL) {
377                 return g_strdup(_("None"));
378         }
379
380         g_return_val_if_fail (GL_IS_MERGE (merge), g_strdup(_("None")));
381
382         return g_strdup(merge->priv->description);
383 }
384
385 /*****************************************************************************/
386 /* Get source type of merge.                                                 */
387 /*****************************************************************************/
388 glMergeSrcType
389 gl_merge_get_src_type (glMerge *merge)
390 {
391         gl_debug (DEBUG_MERGE, "");
392
393         if (merge == NULL) {
394                 return GL_MERGE_SRC_IS_FIXED;
395         }
396
397         g_return_val_if_fail (GL_IS_MERGE (merge), GL_MERGE_SRC_IS_FIXED);
398
399         return merge->priv->src_type;
400 }
401
402 /*****************************************************************************/
403 /* Set src of merge.                                                         */
404 /*****************************************************************************/
405 void
406 gl_merge_set_src (glMerge *merge,
407                   gchar   *src)
408 {
409         GList         *record_list = NULL;
410         glMergeRecord *record;
411
412         gl_debug (DEBUG_MERGE, "START");
413
414         if (merge == NULL)
415         {
416                 gl_debug (DEBUG_MERGE, "END (NULL)");
417                 return;
418         }
419
420         g_return_if_fail (GL_IS_MERGE (merge));
421
422         if ( src == NULL)
423         {
424
425                 if ( merge->priv->src != NULL )
426                 {
427                         g_free (merge->priv->src);
428                 }
429                 merge->priv->src = NULL;
430                 merge_free_record_list (&merge->priv->record_list);
431
432         }
433         else
434         {
435
436                 if ( merge->priv->src != NULL )
437                 {
438                         g_free(merge->priv->src);
439                 }
440                 merge->priv->src = g_strdup (src);
441
442                 merge_free_record_list (&merge->priv->record_list);
443                         
444                 merge_open (merge);
445                 while ( (record = merge_get_record (merge)) != NULL )
446                 {
447                         record_list = g_list_append( record_list, record );
448                 }
449                 merge_close (merge);
450                 merge->priv->record_list = record_list;
451
452         }
453                      
454
455         gl_debug (DEBUG_MERGE, "END");
456 }
457
458 /*****************************************************************************/
459 /* Get src of merge.                                                         */
460 /*****************************************************************************/
461 gchar *
462 gl_merge_get_src (glMerge *merge)
463 {
464         gl_debug (DEBUG_MERGE, "");
465
466         if (merge == NULL) {
467                 return NULL;
468         }
469
470         g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
471
472         return g_strdup(merge->priv->src);
473 }
474
475 /*****************************************************************************/
476 /* Get Key List.                                                             */
477 /*****************************************************************************/
478 GList *
479 gl_merge_get_key_list (glMerge *merge)
480 {
481         GList *key_list = NULL;
482
483         gl_debug (DEBUG_MERGE, "START");
484
485         if (merge == NULL) {
486                 return NULL;
487         }
488
489         g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
490
491         if ( GL_MERGE_GET_CLASS(merge)->get_key_list != NULL ) {
492
493                 key_list = GL_MERGE_GET_CLASS(merge)->get_key_list (merge);
494
495         }
496
497         gl_debug (DEBUG_MERGE, "END");
498
499         return key_list;
500 }
501
502 /*****************************************************************************/
503 /* Free a list of keys.                                                      */
504 /*****************************************************************************/
505 void
506 gl_merge_free_key_list (GList **key_list)
507 {
508         GList *p;
509
510         gl_debug (DEBUG_MERGE, "START");
511
512         for (p = *key_list; p != NULL; p = p->next) {
513                 g_free (p->data);
514                 p->data = NULL;
515         }
516
517         g_list_free (*key_list);
518         *key_list = NULL;
519
520         gl_debug (DEBUG_MERGE, "END");
521 }
522
523 /*****************************************************************************/
524 /* Get Key List.                                                             */
525 /*****************************************************************************/
526 gchar *
527 gl_merge_get_primary_key (glMerge *merge)
528 {
529         gchar *key = NULL;
530
531         gl_debug (DEBUG_MERGE, "START");
532
533         if (merge == NULL) {
534                 return NULL;
535         }
536
537         g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
538
539         if ( GL_MERGE_GET_CLASS(merge)->get_primary_key != NULL ) {
540
541                 key = GL_MERGE_GET_CLASS(merge)->get_primary_key (merge);
542
543         }
544
545         gl_debug (DEBUG_MERGE, "END");
546
547         return key;
548 }
549
550 /*---------------------------------------------------------------------------*/
551 /* Open merge source.                                                        */
552 /*---------------------------------------------------------------------------*/
553 static void
554 merge_open (glMerge *merge)
555 {
556         gl_debug (DEBUG_MERGE, "START");
557
558         g_return_if_fail (merge && GL_IS_MERGE (merge));
559
560         if ( GL_MERGE_GET_CLASS(merge)->open != NULL ) {
561
562                 GL_MERGE_GET_CLASS(merge)->open (merge);
563
564         }
565
566         gl_debug (DEBUG_MERGE, "END");
567 }
568
569 /*---------------------------------------------------------------------------*/
570 /* Close merge source.                                                       */
571 /*---------------------------------------------------------------------------*/
572 static void
573 merge_close (glMerge *merge)
574 {
575         gl_debug (DEBUG_MERGE, "START");
576
577         g_return_if_fail (merge && GL_IS_MERGE (merge));
578
579         if ( GL_MERGE_GET_CLASS(merge)->close != NULL ) {
580
581                 GL_MERGE_GET_CLASS(merge)->close (merge);
582
583         }
584
585         gl_debug (DEBUG_MERGE, "END");
586 }
587
588 /*---------------------------------------------------------------------------*/
589 /* Get next record (list of fields) from opened merge source.                */
590 /*---------------------------------------------------------------------------*/
591 static glMergeRecord *
592 merge_get_record (glMerge *merge)
593 {
594         glMergeRecord *record = NULL;
595
596         gl_debug (DEBUG_MERGE, "START");
597
598         g_return_val_if_fail (merge && GL_IS_MERGE (merge), NULL);
599
600         if ( GL_MERGE_GET_CLASS(merge)->get_record != NULL ) {
601
602                 record = GL_MERGE_GET_CLASS(merge)->get_record (merge);
603
604         }
605
606         gl_debug (DEBUG_MERGE, "END");
607
608         return record;
609 }
610
611 /*---------------------------------------------------------------------------*/
612 /* Free a merge record (list of fields)                                      */
613 /*---------------------------------------------------------------------------*/
614 static void
615 merge_free_record (glMergeRecord **record)
616 {
617         GList *p;
618         glMergeField *field;
619
620         gl_debug (DEBUG_MERGE, "START");
621
622         for (p = (*record)->field_list; p != NULL; p = p->next) {
623                 field = (glMergeField *) p->data;
624
625                 g_free (field->key);
626                 field->key = NULL;
627                 g_free (field->value);
628                 field->value = NULL;
629
630                 g_free (p->data);
631                 p->data = NULL;
632
633         }
634         g_list_free ((*record)->field_list);
635         (*record)->field_list = NULL;
636
637         g_free (*record);
638         *record = NULL;
639
640         gl_debug (DEBUG_MERGE, "END");
641 }
642
643 /*---------------------------------------------------------------------------*/
644 /* Duplicate a merge record (list of fields)                                 */
645 /*---------------------------------------------------------------------------*/
646 static glMergeRecord *
647 merge_dup_record (glMergeRecord *record)
648 {
649         glMergeRecord *dest_record;
650         GList         *p;
651         glMergeField  *dest_field, *field;
652
653         gl_debug (DEBUG_MERGE, "START");
654
655         dest_record = g_new0 (glMergeRecord, 1);
656         dest_record->select_flag = record->select_flag;
657
658         for (p = record->field_list; p != NULL; p = p->next) {
659                 field = (glMergeField *) p->data;
660
661                 dest_field = g_new0 (glMergeField, 1);
662
663                 dest_field->key   = g_strdup (field->key);
664                 dest_field->value = g_strdup (field->value);
665
666                 dest_record->field_list =
667                         g_list_append (dest_record->field_list, dest_field);
668
669         }
670
671         gl_debug (DEBUG_MERGE, "END");
672
673         return dest_record;
674 }
675
676 /*****************************************************************************/
677 /* Find key in given record and evaluate.                                    */
678 /*****************************************************************************/
679 gchar *
680 gl_merge_eval_key (glMergeRecord *record,
681                    gchar         *key)
682                    
683 {
684         GList        *p;
685         glMergeField *field;
686         gchar        *val = NULL;
687
688         gl_debug (DEBUG_MERGE, "START");
689
690         if ( (record != NULL) ) {
691                 for (p = record->field_list; p != NULL; p = p->next) {
692                         field = (glMergeField *) p->data;
693
694                         if (strcmp (key, field->key) == 0) {
695                                 val = g_strdup (field->value);
696                         }
697
698                 }
699         }
700
701         gl_debug (DEBUG_MERGE, "END");
702
703         return val;
704 }
705
706 /*****************************************************************************/
707 /* Read all records from merge source.                                       */
708 /*****************************************************************************/
709 const GList *
710 gl_merge_get_record_list (glMerge *merge)
711 {
712         gl_debug (DEBUG_MERGE, "");
713               
714         if ( merge != NULL ) {
715                 return merge->priv->record_list;
716         } else {
717                 return NULL;
718         }
719 }
720
721 /*---------------------------------------------------------------------------*/
722 /* Free a list of records.                                                   */
723 /*---------------------------------------------------------------------------*/
724 static void
725 merge_free_record_list (GList **record_list)
726 {
727         GList *p;
728         glMergeRecord *record;
729
730         gl_debug (DEBUG_MERGE, "START");
731
732         for (p = *record_list; p != NULL; p = p->next) {
733                 record = (glMergeRecord *) p->data;
734
735                 merge_free_record( &record );
736
737         }
738
739         g_list_free (*record_list);
740         *record_list = NULL;
741
742         gl_debug (DEBUG_MERGE, "END");
743 }
744
745 /*---------------------------------------------------------------------------*/
746 /* Duplicate a list of records.                                              */
747 /*---------------------------------------------------------------------------*/
748 static GList *
749 merge_dup_record_list (GList *record_list)
750 {
751         GList         *dest_list = NULL, *p;
752         glMergeRecord *dest_record, *record;
753
754         gl_debug (DEBUG_MERGE, "START");
755
756         for (p = record_list; p != NULL; p = p->next) {
757                 record = (glMergeRecord *) p->data;
758
759                 dest_record = merge_dup_record( record );
760                 dest_list = g_list_append (dest_list, dest_record);
761         }
762
763
764         gl_debug (DEBUG_MERGE, "END");
765
766         return dest_list;
767 }
768
769 /*****************************************************************************/
770 /* Count selected records.                                                   */
771 /*****************************************************************************/
772 gint
773 gl_merge_get_record_count (glMerge *merge)
774 {
775         GList *p;
776         glMergeRecord *record;
777         gint count;
778
779         gl_debug (DEBUG_MERGE, "START");
780
781         count = 0;
782         for ( p=merge->priv->record_list; p!=NULL; p=p->next ) {
783                 record = (glMergeRecord *)p->data;
784
785                 if ( record->select_flag ) count ++;
786         }
787
788         gl_debug (DEBUG_MERGE, "END");
789
790         return count;
791 }
792
793