]> git.sur5r.net Git - glabels/blob - src/merge.c
Imported Upstream version 3.0.0
[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.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       (const 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         /* Translators: "None" here means that no document-merge source or
170          * method has been selected. */
171         descriptions = g_list_append (descriptions, g_strdup(_("None")));
172
173         for ( p=backends; p!=NULL; p=p->next) {
174                 backend = (Backend *)p->data;
175                 descriptions = g_list_append (descriptions,
176                                               g_strdup(backend->description));
177         }
178
179         return descriptions;
180 }
181
182 /*****************************************************************************/
183 /* Free list of descriptions.                                                */
184 /*****************************************************************************/
185 void
186 gl_merge_free_descriptions (GList **descriptions)
187 {
188         GList *p;
189
190         gl_debug (DEBUG_MERGE, "START");
191
192         for (p = *descriptions; p != NULL; p = p->next) {
193                 g_free (p->data);
194                 p->data = NULL;
195         }
196
197         g_list_free (*descriptions);
198         *descriptions = NULL;
199
200         gl_debug (DEBUG_MERGE, "END");
201 }
202
203 /*****************************************************************************/
204 /* Lookup name of backend from description.                                  */
205 /*****************************************************************************/
206 gchar *
207 gl_merge_description_to_name (gchar *description)
208 {
209         GList   *p;
210         Backend *backend;
211
212         if (lgl_str_utf8_casecmp(description, _("None")) == 0) {
213                 return g_strdup("None");
214         }
215
216         for ( p=backends; p!=NULL; p=p->next) {
217                 backend = (Backend *)p->data;
218                 if (lgl_str_utf8_casecmp(description, backend->description) == 0) {
219                         return g_strdup(backend->name);
220                 }
221         }
222
223         return g_strdup("None");
224 }
225
226 /*****************************************************************************/
227 /* Boilerplate object stuff.                                                 */
228 /*****************************************************************************/
229 G_DEFINE_TYPE (glMerge, gl_merge, G_TYPE_OBJECT)
230
231 static void
232 gl_merge_class_init (glMergeClass *class)
233 {
234         GObjectClass *object_class = G_OBJECT_CLASS (class);
235
236         gl_debug (DEBUG_MERGE, "START");
237
238         gl_merge_parent_class = g_type_class_peek_parent (class);
239
240         object_class->finalize = gl_merge_finalize;
241
242         gl_debug (DEBUG_MERGE, "END");
243 }
244
245 static void
246 gl_merge_init (glMerge *merge)
247 {
248         gl_debug (DEBUG_MERGE, "START");
249
250         merge->priv = g_new0 (glMergePrivate, 1);
251
252         gl_debug (DEBUG_MERGE, "END");
253 }
254
255 static void
256 gl_merge_finalize (GObject *object)
257 {
258         glMerge *merge = GL_MERGE (object);
259
260         gl_debug (DEBUG_MERGE, "START");
261
262         g_return_if_fail (object && GL_IS_MERGE (object));
263
264         merge_free_record_list (&merge->priv->record_list);
265         g_free (merge->priv->name);
266         g_free (merge->priv->description);
267         g_free (merge->priv->src);
268         g_free (merge->priv);
269
270         G_OBJECT_CLASS (gl_merge_parent_class)->finalize (object);
271
272         gl_debug (DEBUG_MERGE, "END");
273 }
274
275 /*****************************************************************************/
276 /* New merge object.                                                         */
277 /*****************************************************************************/
278 glMerge *
279 gl_merge_new (const gchar *name)
280 {
281         glMerge *merge = NULL;
282         GList   *p;
283         Backend *backend;
284
285         gl_debug (DEBUG_MERGE, "START");
286
287         for (p=backends; p!=NULL; p=p->next) {
288                 backend = (Backend *)p->data;
289
290                 if (g_ascii_strcasecmp(name, backend->name) == 0) {
291
292                         merge = GL_MERGE (g_object_newv (backend->type,
293                                                          backend->n_params,
294                                                          backend->params));
295
296                         merge->priv->name        = g_strdup (name);
297                         merge->priv->description = g_strdup (backend->description);
298                         merge->priv->src_type    = backend->src_type;
299
300                         break;
301                 }
302         }
303
304         if ( (merge == NULL) && (g_ascii_strcasecmp (name, "None") != 0)) {
305                 g_message ("Unknown merge backend \"%s\"", name);
306         }
307
308         gl_debug (DEBUG_MERGE, "END");
309
310         return merge;
311 }
312
313 /*****************************************************************************/
314 /* Duplicate merge.                                                         */
315 /*****************************************************************************/
316 glMerge *
317 gl_merge_dup (const glMerge *src_merge)
318 {
319         glMerge    *dst_merge;
320
321         gl_debug (DEBUG_MERGE, "START");
322
323         if (src_merge == NULL) {
324                 gl_debug (DEBUG_MERGE, "END (NULL)");
325                 return NULL;
326         }
327
328         g_return_val_if_fail (GL_IS_MERGE (src_merge), NULL);
329
330         dst_merge = g_object_new (G_OBJECT_TYPE(src_merge), NULL);
331         dst_merge->priv->name        = g_strdup (src_merge->priv->name);
332         dst_merge->priv->description = g_strdup (src_merge->priv->description);
333         dst_merge->priv->src         = g_strdup (src_merge->priv->src);
334         dst_merge->priv->src_type    = src_merge->priv->src_type;
335         dst_merge->priv->record_list 
336                 = merge_dup_record_list (src_merge->priv->record_list);
337
338         if ( GL_MERGE_GET_CLASS(src_merge)->copy != NULL ) {
339
340                 /* We have an object specific method, use it */
341                 GL_MERGE_GET_CLASS(src_merge)->copy (dst_merge, src_merge);
342
343         }
344
345         gl_debug (DEBUG_MERGE, "END");
346
347         return dst_merge;
348 }
349
350 /*****************************************************************************/
351 /* Get name of merge.                                                        */
352 /*****************************************************************************/
353 gchar *
354 gl_merge_get_name (const glMerge *merge)
355 {
356         gl_debug (DEBUG_MERGE, "");
357
358         if (merge == NULL) {
359                 return g_strdup("None");
360         }
361
362         g_return_val_if_fail (GL_IS_MERGE (merge), g_strdup("None"));
363
364         return g_strdup(merge->priv->name);
365 }
366
367 /*****************************************************************************/
368 /* Get description of merge.                                                 */
369 /*****************************************************************************/
370 gchar *
371 gl_merge_get_description (const glMerge *merge)
372 {
373         gl_debug (DEBUG_MERGE, "");
374
375         if (merge == NULL) {
376                 return g_strdup(_("None"));
377         }
378
379         g_return_val_if_fail (GL_IS_MERGE (merge), g_strdup(_("None")));
380
381         return g_strdup(merge->priv->description);
382 }
383
384 /*****************************************************************************/
385 /* Get source type of merge.                                                 */
386 /*****************************************************************************/
387 glMergeSrcType
388 gl_merge_get_src_type (const glMerge *merge)
389 {
390         gl_debug (DEBUG_MERGE, "");
391
392         if (merge == NULL) {
393                 return GL_MERGE_SRC_IS_FIXED;
394         }
395
396         g_return_val_if_fail (GL_IS_MERGE (merge), GL_MERGE_SRC_IS_FIXED);
397
398         return merge->priv->src_type;
399 }
400
401 /*****************************************************************************/
402 /* Set src of merge.                                                         */
403 /*****************************************************************************/
404 void
405 gl_merge_set_src (glMerge       *merge,
406                   const gchar   *src)
407 {
408         GList         *record_list = NULL;
409         glMergeRecord *record;
410
411         gl_debug (DEBUG_MERGE, "START");
412
413         if (merge == NULL)
414         {
415                 gl_debug (DEBUG_MERGE, "END (NULL)");
416                 return;
417         }
418
419         g_return_if_fail (GL_IS_MERGE (merge));
420
421         if ( src == NULL)
422         {
423
424                 if ( merge->priv->src != NULL )
425                 {
426                         g_free (merge->priv->src);
427                 }
428                 merge->priv->src = NULL;
429                 merge_free_record_list (&merge->priv->record_list);
430
431         }
432         else
433         {
434
435                 if ( merge->priv->src != NULL )
436                 {
437                         g_free(merge->priv->src);
438                 }
439                 merge->priv->src = g_strdup (src);
440
441                 merge_free_record_list (&merge->priv->record_list);
442                         
443                 merge_open (merge);
444                 while ( (record = merge_get_record (merge)) != NULL )
445                 {
446                         record_list = g_list_append( record_list, record );
447                 }
448                 merge_close (merge);
449                 merge->priv->record_list = record_list;
450
451         }
452                      
453
454         gl_debug (DEBUG_MERGE, "END");
455 }
456
457 /*****************************************************************************/
458 /* Get src of merge.                                                         */
459 /*****************************************************************************/
460 gchar *
461 gl_merge_get_src (const glMerge *merge)
462 {
463         gl_debug (DEBUG_MERGE, "");
464
465         if (merge == NULL) {
466                 return NULL;
467         }
468
469         g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
470
471         return g_strdup(merge->priv->src);
472 }
473
474 /*****************************************************************************/
475 /* Get Key List.                                                             */
476 /*****************************************************************************/
477 GList *
478 gl_merge_get_key_list (const glMerge *merge)
479 {
480         GList *key_list = NULL;
481
482         gl_debug (DEBUG_MERGE, "START");
483
484         if (merge == NULL) {
485                 return NULL;
486         }
487
488         g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
489
490         if ( GL_MERGE_GET_CLASS(merge)->get_key_list != NULL ) {
491
492                 key_list = GL_MERGE_GET_CLASS(merge)->get_key_list (merge);
493
494         }
495
496         gl_debug (DEBUG_MERGE, "END");
497
498         return key_list;
499 }
500
501 /*****************************************************************************/
502 /* Free a list of keys.                                                      */
503 /*****************************************************************************/
504 void
505 gl_merge_free_key_list (GList **key_list)
506 {
507         GList *p;
508
509         gl_debug (DEBUG_MERGE, "START");
510
511         for (p = *key_list; p != NULL; p = p->next) {
512                 g_free (p->data);
513                 p->data = NULL;
514         }
515
516         g_list_free (*key_list);
517         *key_list = NULL;
518
519         gl_debug (DEBUG_MERGE, "END");
520 }
521
522 /*****************************************************************************/
523 /* Get Key List.                                                             */
524 /*****************************************************************************/
525 gchar *
526 gl_merge_get_primary_key (const glMerge *merge)
527 {
528         gchar *key = NULL;
529
530         gl_debug (DEBUG_MERGE, "START");
531
532         if (merge == NULL) {
533                 return NULL;
534         }
535
536         g_return_val_if_fail (GL_IS_MERGE (merge), NULL);
537
538         if ( GL_MERGE_GET_CLASS(merge)->get_primary_key != NULL ) {
539
540                 key = GL_MERGE_GET_CLASS(merge)->get_primary_key (merge);
541
542         }
543
544         gl_debug (DEBUG_MERGE, "END");
545
546         return key;
547 }
548
549 /*---------------------------------------------------------------------------*/
550 /* Open merge source.                                                        */
551 /*---------------------------------------------------------------------------*/
552 static void
553 merge_open (glMerge *merge)
554 {
555         gl_debug (DEBUG_MERGE, "START");
556
557         g_return_if_fail (merge && GL_IS_MERGE (merge));
558
559         if ( GL_MERGE_GET_CLASS(merge)->open != NULL ) {
560
561                 GL_MERGE_GET_CLASS(merge)->open (merge);
562
563         }
564
565         gl_debug (DEBUG_MERGE, "END");
566 }
567
568 /*---------------------------------------------------------------------------*/
569 /* Close merge source.                                                       */
570 /*---------------------------------------------------------------------------*/
571 static void
572 merge_close (glMerge *merge)
573 {
574         gl_debug (DEBUG_MERGE, "START");
575
576         g_return_if_fail (merge && GL_IS_MERGE (merge));
577
578         if ( GL_MERGE_GET_CLASS(merge)->close != NULL ) {
579
580                 GL_MERGE_GET_CLASS(merge)->close (merge);
581
582         }
583
584         gl_debug (DEBUG_MERGE, "END");
585 }
586
587 /*---------------------------------------------------------------------------*/
588 /* Get next record (list of fields) from opened merge source.                */
589 /*---------------------------------------------------------------------------*/
590 static glMergeRecord *
591 merge_get_record (glMerge *merge)
592 {
593         glMergeRecord *record = NULL;
594
595         gl_debug (DEBUG_MERGE, "START");
596
597         g_return_val_if_fail (merge && GL_IS_MERGE (merge), NULL);
598
599         if ( GL_MERGE_GET_CLASS(merge)->get_record != NULL ) {
600
601                 record = GL_MERGE_GET_CLASS(merge)->get_record (merge);
602
603         }
604
605         gl_debug (DEBUG_MERGE, "END");
606
607         return record;
608 }
609
610 /*---------------------------------------------------------------------------*/
611 /* Free a merge record (list of fields)                                      */
612 /*---------------------------------------------------------------------------*/
613 static void
614 merge_free_record (glMergeRecord **record)
615 {
616         GList *p;
617         glMergeField *field;
618
619         gl_debug (DEBUG_MERGE, "START");
620
621         for (p = (*record)->field_list; p != NULL; p = p->next) {
622                 field = (glMergeField *) p->data;
623
624                 g_free (field->key);
625                 field->key = NULL;
626                 g_free (field->value);
627                 field->value = NULL;
628
629                 g_free (p->data);
630                 p->data = NULL;
631
632         }
633         g_list_free ((*record)->field_list);
634         (*record)->field_list = NULL;
635
636         g_free (*record);
637         *record = NULL;
638
639         gl_debug (DEBUG_MERGE, "END");
640 }
641
642 /*---------------------------------------------------------------------------*/
643 /* Duplicate a merge record (list of fields)                                 */
644 /*---------------------------------------------------------------------------*/
645 static glMergeRecord *
646 merge_dup_record (const glMergeRecord *record)
647 {
648         glMergeRecord *dest_record;
649         GList         *p;
650         glMergeField  *dest_field, *field;
651
652         gl_debug (DEBUG_MERGE, "START");
653
654         dest_record = g_new0 (glMergeRecord, 1);
655         dest_record->select_flag = record->select_flag;
656
657         for (p = record->field_list; p != NULL; p = p->next) {
658                 field = (glMergeField *) p->data;
659
660                 dest_field = g_new0 (glMergeField, 1);
661
662                 dest_field->key   = g_strdup (field->key);
663                 dest_field->value = g_strdup (field->value);
664
665                 dest_record->field_list =
666                         g_list_append (dest_record->field_list, dest_field);
667
668         }
669
670         gl_debug (DEBUG_MERGE, "END");
671
672         return dest_record;
673 }
674
675 /*****************************************************************************/
676 /* Find key in given record and evaluate.                                    */
677 /*****************************************************************************/
678 gchar *
679 gl_merge_eval_key (const glMergeRecord *record,
680                    const gchar         *key)
681                    
682 {
683         GList        *p;
684         glMergeField *field;
685         gchar        *val = NULL;
686
687         gl_debug (DEBUG_MERGE, "START");
688
689         if ( (record != NULL) && (key != NULL) ) {
690                 for (p = record->field_list; p != NULL; p = p->next) {
691                         field = (glMergeField *) p->data;
692
693                         if (strcmp (key, field->key) == 0) {
694                                 val = g_strdup (field->value);
695                         }
696
697                 }
698         }
699
700         gl_debug (DEBUG_MERGE, "END");
701
702         return val;
703 }
704
705 /*****************************************************************************/
706 /* Read all records from merge source.                                       */
707 /*****************************************************************************/
708 const GList *
709 gl_merge_get_record_list (const glMerge *merge)
710 {
711         gl_debug (DEBUG_MERGE, "");
712               
713         if ( merge != NULL ) {
714                 return merge->priv->record_list;
715         } else {
716                 return NULL;
717         }
718 }
719
720 /*---------------------------------------------------------------------------*/
721 /* Free a list of records.                                                   */
722 /*---------------------------------------------------------------------------*/
723 static void
724 merge_free_record_list (GList **record_list)
725 {
726         GList *p;
727         glMergeRecord *record;
728
729         gl_debug (DEBUG_MERGE, "START");
730
731         for (p = *record_list; p != NULL; p = p->next) {
732                 record = (glMergeRecord *) p->data;
733
734                 merge_free_record( &record );
735
736         }
737
738         g_list_free (*record_list);
739         *record_list = NULL;
740
741         gl_debug (DEBUG_MERGE, "END");
742 }
743
744 /*---------------------------------------------------------------------------*/
745 /* Duplicate a list of records.                                              */
746 /*---------------------------------------------------------------------------*/
747 static GList *
748 merge_dup_record_list (GList *record_list)
749 {
750         GList         *dest_list = NULL, *p;
751         glMergeRecord *dest_record, *record;
752
753         gl_debug (DEBUG_MERGE, "START");
754
755         for (p = record_list; p != NULL; p = p->next) {
756                 record = (glMergeRecord *) p->data;
757
758                 dest_record = merge_dup_record( record );
759                 dest_list = g_list_append (dest_list, dest_record);
760         }
761
762
763         gl_debug (DEBUG_MERGE, "END");
764
765         return dest_list;
766 }
767
768 /*****************************************************************************/
769 /* Count selected records.                                                   */
770 /*****************************************************************************/
771 gint
772 gl_merge_get_record_count (const glMerge *merge)
773 {
774         GList *p;
775         glMergeRecord *record;
776         gint count;
777
778         gl_debug (DEBUG_MERGE, "START");
779
780         count = 0;
781         for ( p=merge->priv->record_list; p!=NULL; p=p->next ) {
782                 record = (glMergeRecord *)p->data;
783
784                 if ( record->select_flag ) count ++;
785         }
786
787         gl_debug (DEBUG_MERGE, "END");
788
789         return count;
790 }
791
792
793
794 /*
795  * Local Variables:       -- emacs
796  * mode: C                -- emacs
797  * c-basic-offset: 8      -- emacs
798  * tab-width: 8           -- emacs
799  * indent-tabs-mode: nil  -- emacs
800  * End:                   -- emacs
801  */