Tidied up a bit
[pspp] / src / data / gnumeric-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
3
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <http://www.gnu.org/licenses/>. */
16
17 #include <config.h>
18
19 #include "libpspp/message.h"
20 #include "libpspp/misc.h"
21
22 #include "gl/minmax.h"
23 #include "gl/c-strtod.h"
24
25 #include "gettext.h"
26 #define _(msgid) gettext (msgid)
27 #define N_(msgid) (msgid)
28
29 #include "spreadsheet-reader.h"
30
31 #include "c-xvasprintf.h"
32
33 #if !GNM_SUPPORT
34
35 struct casereader *
36 gnumeric_open_reader (struct spreadsheet_read_info *gri, struct spreadsheet_read_options *opts, struct dictionary **dict)
37 {
38   msg (ME, _("Support for %s files was not compiled into this installation of PSPP"), "Gnumeric");
39
40   return NULL;
41 }
42
43 #else
44
45 #include "data/gnumeric-reader.h"
46
47 #include <assert.h>
48 #include <stdbool.h>
49 #include <errno.h>
50 #include <libxml/xmlreader.h>
51 #include <zlib.h>
52
53 #include "data/case.h"
54 #include "data/casereader-provider.h"
55 #include "data/dictionary.h"
56 #include "data/identifier.h"
57 #include "data/value.h"
58 #include "data/variable.h"
59 #include "libpspp/i18n.h"
60 #include "libpspp/str.h"
61
62 #include "gl/xalloc.h"
63
64 static void gnm_file_casereader_destroy (struct casereader *, void *);
65
66 static struct ccase *gnm_file_casereader_read (struct casereader *, void *);
67
68 static const struct casereader_class gnm_file_casereader_class =
69   {
70     gnm_file_casereader_read,
71     gnm_file_casereader_destroy,
72     NULL,
73     NULL,
74   };
75
76 enum reader_state
77   {
78     STATE_PRE_INIT = 0,        /* Initial state */
79     STATE_SHEET_COUNT,      /* Found the sheet index */
80     STATE_INIT ,           /* Other Initial state */
81     STATE_SHEET_START,     /* Found the start of a sheet */
82     STATE_SHEET_NAME,      /* Found the sheet name */
83     STATE_MAXROW,
84     STATE_SHEET_FOUND,     /* Found the sheet that we actually want */
85     STATE_CELLS_START,     /* Found the start of the cell array */
86     STATE_CELL             /* Found a cell */
87   };
88
89
90 struct gnumeric_reader
91 {
92   struct spreadsheet spreadsheet;
93
94   xmlTextReaderPtr xtr;
95
96   enum reader_state state;
97
98   int row;
99   int col;
100   int min_col;
101   int node_type;
102   int sheet_index;
103   
104   xmlChar **sheet_names;
105
106   const xmlChar *target_sheet;
107   int target_sheet_index;
108
109   int start_row;
110   int start_col;
111   int stop_row;
112   int stop_col;
113
114   struct caseproto *proto;
115   struct dictionary *dict;
116   struct ccase *first_case;
117   bool used_first_case;
118 };
119
120
121 const char *
122 gnumeric_get_sheet_name (struct spreadsheet *s, int n)
123 {
124   struct gnumeric_reader *gr = (struct gnumeric_reader *) s;
125   assert (n < s->sheets);
126
127   return gr->sheet_names[n];
128 }
129
130 const char *
131 gnumeric_get_sheet_range (struct spreadsheet *s, int n)
132 {
133   struct gnumeric_reader *gr = (struct gnumeric_reader *) s;
134   assert (n < s->sheets);
135
136   return "I havent the fogiest idea";
137 }
138
139
140 static void process_node (struct gnumeric_reader *r);
141
142
143 static void
144 gnm_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
145 {
146   int i;
147   struct gnumeric_reader *r = r_;
148   if ( r == NULL)
149         return ;
150
151   if ( r->xtr)
152     xmlFreeTextReader (r->xtr);
153
154   if ( ! r->used_first_case )
155     case_unref (r->first_case);
156
157   caseproto_unref (r->proto);
158
159   for (i = 0; i < r->spreadsheet.sheets; ++i)
160     {
161       xmlFree (r->sheet_names[i]);
162     }
163     
164   free (r->sheet_names);
165
166   free (r);
167 }
168
169 static void
170 process_node (struct gnumeric_reader *r)
171 {
172   xmlChar *name = xmlTextReaderName (r->xtr);
173   if (name == NULL)
174     name = xmlStrdup (_xml ("--"));
175
176
177   r->node_type = xmlTextReaderNodeType (r->xtr);
178
179   switch ( r->state)
180     {
181     case STATE_PRE_INIT:
182       if (0 == xmlStrcasecmp (name, _xml("gnm:SheetNameIndex")) &&
183           XML_READER_TYPE_ELEMENT  == r->node_type)
184         {
185           r->state = STATE_SHEET_COUNT;
186           r->spreadsheet.sheets = 0;
187         }
188       break;
189
190     case STATE_SHEET_COUNT:
191       if (0 == xmlStrcasecmp (name, _xml("gnm:SheetName")) &&
192           XML_READER_TYPE_ELEMENT  == r->node_type)
193         {
194           r->spreadsheet.sheets++;
195           r->sheet_names = xrealloc (r->sheet_names, r->spreadsheet.sheets * sizeof *r->sheet_names);
196         }
197       else if (0 == xmlStrcasecmp (name, _xml("gnm:SheetNameIndex")) &&
198           XML_READER_TYPE_END_ELEMENT  == r->node_type)
199         {
200           r->state = STATE_INIT;
201         }
202       else if (XML_READER_TYPE_TEXT == r->node_type)
203         {
204           r->sheet_names [r->spreadsheet.sheets - 1] = xmlTextReaderValue (r->xtr);
205         }
206       break;
207
208     case STATE_INIT:
209       if (0 == xmlStrcasecmp (name, _xml("gnm:Sheet")) &&
210           XML_READER_TYPE_ELEMENT  == r->node_type)
211         {
212           ++r->sheet_index;
213           r->state = STATE_SHEET_START;
214         }
215       break;
216     case STATE_SHEET_START:
217       if (0 == xmlStrcasecmp (name, _xml("gnm:Name"))  &&
218           XML_READER_TYPE_ELEMENT  == r->node_type)
219         {
220           r->state = STATE_SHEET_NAME;
221         }
222       break;
223     case STATE_SHEET_NAME:
224       if (0 == xmlStrcasecmp (name, _xml("gnm:Name"))  &&
225           XML_READER_TYPE_END_ELEMENT  == r->node_type)
226         {
227           r->state = STATE_INIT;
228         }
229       else if (XML_READER_TYPE_TEXT == r->node_type)
230         {
231           if ( r->target_sheet != NULL)
232             {
233               xmlChar *value = xmlTextReaderValue (r->xtr);
234               if ( 0 == xmlStrcmp (value, r->target_sheet))
235                 r->state = STATE_SHEET_FOUND;
236               free (value);
237             }
238           else if (r->target_sheet_index == r->sheet_index)
239             {
240               r->state = STATE_SHEET_FOUND;
241             }
242         }
243       break;
244     case STATE_SHEET_FOUND:
245       if (0 == xmlStrcasecmp (name, _xml("gnm:Cells"))  &&
246           XML_READER_TYPE_ELEMENT  == r->node_type)
247         {
248           r->min_col = INT_MAX;
249           if (! xmlTextReaderIsEmptyElement (r->xtr))
250             r->state = STATE_CELLS_START;
251         }
252       else if (0 == xmlStrcasecmp (name, _xml("gnm:MaxRow"))  &&
253           XML_READER_TYPE_ELEMENT  == r->node_type)
254         {
255           r->state = STATE_MAXROW;
256         }
257       else if (0 == xmlStrcasecmp (name, _xml("gnm:Sheet"))  &&
258           XML_READER_TYPE_END_ELEMENT  == r->node_type)
259         {
260           r->state = STATE_INIT;
261         }
262       break;
263     case STATE_MAXROW:
264       if (0 == xmlStrcasecmp (name, _xml("gnm:MaxRow"))  &&
265           XML_READER_TYPE_END_ELEMENT  == r->node_type)
266         {
267           r->state = STATE_SHEET_FOUND;
268         }
269     case STATE_CELLS_START:
270       if (0 == xmlStrcasecmp (name, _xml ("gnm:Cell"))  &&
271           XML_READER_TYPE_ELEMENT  == r->node_type)
272         {
273           xmlChar *attr = NULL;
274           r->state = STATE_CELL;
275
276           attr = xmlTextReaderGetAttribute (r->xtr, _xml ("Col"));
277           r->col =  _xmlchar_to_int (attr);
278           free (attr);
279
280           if (r->col < r->min_col)
281             r->min_col = r->col;
282
283           attr = xmlTextReaderGetAttribute (r->xtr, _xml ("Row"));
284           r->row = _xmlchar_to_int (attr);
285           free (attr);
286         }
287       else if (0 == xmlStrcasecmp (name, _xml("gnm:Cells"))  &&
288                XML_READER_TYPE_END_ELEMENT  == r->node_type)
289         r->state = STATE_SHEET_NAME;
290
291       break;
292     case STATE_CELL:
293       if (0 == xmlStrcasecmp (name, _xml("gnm:Cell"))  &&
294                               XML_READER_TYPE_END_ELEMENT  == r->node_type)
295         {
296           r->state = STATE_CELLS_START;
297         }
298       break;
299     default:
300       break;
301     };
302
303   xmlFree (name);
304 }
305
306
307 /*
308    Sets the VAR of case C, to the value corresponding to the xml string XV
309  */
310 static void
311 convert_xml_string_to_value (struct ccase *c, const struct variable *var,
312                              const xmlChar *xv)
313 {
314   union value *v = case_data_rw (c, var);
315
316   if (xv == NULL)
317     value_set_missing (v, var_get_width (var));
318   else if ( var_is_alpha (var))
319     value_copy_str_rpad (v, var_get_width (var), xv, ' ');
320   else
321     {
322       const char *text = CHAR_CAST (const char *, xv);
323       char *endptr;
324
325       errno = 0;
326       v->f = c_strtod (text, &endptr);
327       if ( errno != 0 || endptr == text)
328         v->f = SYSMIS;
329     }
330 }
331
332 struct var_spec
333 {
334   char *name;
335   int width;
336   xmlChar *first_value;
337 };
338
339
340 void 
341 gnumeric_destroy (struct spreadsheet *s)
342 {
343     struct gnumeric_reader *r = s;
344     gnm_file_casereader_destroy (NULL, s);
345 }
346
347 struct spreadsheet *
348 gnumeric_probe (const char *filename)
349 {
350   int ret;
351   struct gnumeric_reader *r = NULL;
352   xmlTextReaderPtr xtr;
353
354   gzFile gz = gzopen (filename, "r");
355
356   if (NULL == gz)
357     return NULL;
358
359   xtr = xmlReaderForIO ((xmlInputReadCallback) gzread,
360                            (xmlInputCloseCallback) gzclose, gz,
361                            NULL, NULL, 0);
362
363   if (xtr == NULL)
364     return NULL;
365
366   r = xzalloc (sizeof *r);
367   
368   r->xtr = xtr;
369   r->spreadsheet.sheets = -1;
370   r->state = STATE_PRE_INIT;
371
372
373   /* Advance to the start of the workbook.
374      This gives us some confidence that we are actually dealing with a gnumeric
375      spreadsheet.
376    */
377   while ( (r->state != STATE_INIT )
378           && 1 == (ret = xmlTextReaderRead (r->xtr)))
379     {
380       process_node (r);
381     }
382
383   if (ret != 1)
384     {
385       /* Not a gnumeric spreadsheet */
386       free (r);
387       gzclose (gz);
388       return NULL;
389     }
390     
391   r->spreadsheet.type = SPREADSHEET_GNUMERIC;
392   r->spreadsheet.file_name = filename;
393   
394   return &r->spreadsheet;
395 }
396
397
398 struct casereader *
399 gnumeric_make_reader (struct spreadsheet *spreadsheet,
400                       const struct spreadsheet_read_info *gri, 
401                       struct spreadsheet_read_options *opts)
402 {
403   struct gnumeric_reader *r = NULL;
404   unsigned long int vstart = 0;
405   int ret;
406   casenumber n_cases = CASENUMBER_MAX;
407   int i;
408   struct var_spec *var_spec = NULL;
409   int n_var_specs = 0;
410
411   r = (struct gnumeric_reader *) (spreadsheet);
412
413   if ( opts->cell_range )
414     {
415       if ( ! convert_cell_ref (opts->cell_range,
416                                &r->start_col, &r->start_row,
417                                &r->stop_col, &r->stop_row))
418         {
419           msg (SE, _("Invalid cell range `%s'"),
420                opts->cell_range);
421           goto error;
422         }
423     }
424   else
425     {
426       r->start_col = -1;
427       r->start_row = 0;
428       r->stop_col = -1;
429       r->stop_row = -1;
430     }
431
432
433   r->target_sheet = BAD_CAST opts->sheet_name;
434   r->target_sheet_index = opts->sheet_index;
435   r->row = r->col = -1;
436   r->sheet_index = 0;
437
438   /* Advance to the start of the cells for the target sheet */
439   while ( (r->state != STATE_CELL || r->row < r->start_row )
440           && 1 == (ret = xmlTextReaderRead (r->xtr)))
441     {
442       xmlChar *value ;
443       process_node (r);
444       value = xmlTextReaderValue (r->xtr);
445
446       if ( r->state == STATE_MAXROW  && r->node_type == XML_READER_TYPE_TEXT)
447         {
448           n_cases = 1 + _xmlchar_to_int (value) ;
449         }
450       free (value);
451     }
452
453
454   /* If a range has been given, then  use that to calculate the number
455      of cases */
456   if ( opts->cell_range)
457     {
458       n_cases = MIN (n_cases, r->stop_row - r->start_row + 1);
459     }
460
461   if ( gri->read_names )
462     {
463       r->start_row++;
464       n_cases --;
465     }
466
467   /* Read in the first row of cells,
468      including the headers if read_names was set */
469   while (
470          (( r->state == STATE_CELLS_START && r->row <= r->start_row) || r->state == STATE_CELL )
471          && (ret = xmlTextReaderRead (r->xtr))
472          )
473     {
474       int idx;
475       process_node (r);
476
477       if ( r->row > r->start_row ) break;
478
479       if ( r->col < r->start_col ||
480            (r->stop_col != -1 && r->col > r->stop_col))
481         continue;
482
483       idx = r->col - r->start_col;
484
485       if ( idx  >= n_var_specs )
486         {
487           int i;
488           var_spec = xrealloc (var_spec, sizeof (*var_spec) * (idx + 1));
489           for (i = n_var_specs; i <= idx; ++i)
490           {
491             var_spec [i].name = NULL;
492             var_spec [i].width = -1;
493             var_spec [i].first_value = NULL;
494           }
495           n_var_specs =  idx + 1 ;
496         }
497
498       if ( r->node_type == XML_READER_TYPE_TEXT )
499         {
500           xmlChar *value = xmlTextReaderValue (r->xtr);
501           const char *text  = CHAR_CAST (const char *, value);
502
503           if ( r->row < r->start_row)
504             {
505               if ( gri->read_names )
506                 {
507                   var_spec [idx].name = xstrdup (text);
508                 }
509             }
510           else
511             {
512               var_spec [idx].first_value = xmlStrdup (value);
513
514               if (-1 ==  var_spec [idx].width )
515                 var_spec [idx].width = (gri->asw == -1) ?
516                   ROUND_UP (strlen(text), SPREADSHEET_DEFAULT_WIDTH) : gri->asw;
517             }
518
519           free (value);
520         }
521       else if ( r->node_type == XML_READER_TYPE_ELEMENT
522                 && r->state == STATE_CELL)
523         {
524           if ( r->row == r->start_row )
525             {
526               xmlChar *attr =
527                 xmlTextReaderGetAttribute (r->xtr, _xml ("ValueType"));
528
529               if ( NULL == attr || 60 !=  _xmlchar_to_int (attr))
530                 var_spec [idx].width = 0;
531
532               free (attr);
533             }
534         }
535     }
536
537   {
538     const xmlChar *enc = xmlTextReaderConstEncoding (r->xtr);
539     if ( enc == NULL)
540       goto error;
541     /* Create the dictionary and populate it */
542     spreadsheet->dict = r->dict = dict_create (CHAR_CAST (const char *, enc));
543   }
544
545   for (i = 0 ; i < n_var_specs ; ++i )
546     {
547       char *name;
548
549       if ( (var_spec[i].name == NULL) && (var_spec[i].first_value == NULL))
550         continue;
551
552       /* Probably no data exists for this variable, so allocate a
553          default width */
554       if ( var_spec[i].width == -1 )
555         var_spec[i].width = SPREADSHEET_DEFAULT_WIDTH;
556
557       name = dict_make_unique_var_name (r->dict, var_spec[i].name, &vstart);
558       dict_create_var (r->dict, name, var_spec[i].width);
559       free (name);
560     }
561
562   /* Create the first case, and cache it */
563   r->used_first_case = false;
564
565   if ( n_var_specs ==  0 )
566     {
567       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
568            spreadsheet->file_name);
569       goto error;
570     }
571
572   r->proto = caseproto_ref (dict_get_proto (r->dict));
573   r->first_case = case_create (r->proto);
574   case_set_missing (r->first_case);
575
576   int x = 0;
577   for ( i = 0 ; i < n_var_specs ; ++i )
578     {
579       if ( (var_spec[i].name == NULL) && (var_spec[i].first_value == NULL))
580         continue;
581
582       const struct variable *var = dict_get_var (r->dict, x++);
583
584       convert_xml_string_to_value (r->first_case, var,
585                                    var_spec[i].first_value);
586     }
587
588   for ( i = 0 ; i < n_var_specs ; ++i )
589     {
590       free (var_spec[i].first_value);
591       free (var_spec[i].name);
592     }
593
594   free (var_spec);
595   
596   
597   if (opts->cell_range == NULL)
598     {
599       opts->cell_range = c_xasprintf ("%c%d:%c%ld", 
600                                        r->start_col + 'A',
601                                        r->start_row,
602                                        r->stop_col + 'A' + caseproto_get_n_widths (r->proto),
603                                        r->start_row + n_cases);
604     }
605   
606   return casereader_create_sequential
607     (NULL,
608      r->proto,
609      n_cases,
610      &gnm_file_casereader_class, r);
611
612
613  error:
614   for ( i = 0 ; i < n_var_specs ; ++i )
615     {
616       free (var_spec[i].first_value);
617       free (var_spec[i].name);
618     }
619
620   free (var_spec);
621   dict_destroy (spreadsheet->dict);
622   spreadsheet->dict = NULL;
623
624   gnm_file_casereader_destroy (NULL, r);
625
626   return NULL;
627 };
628
629
630 /* Reads and returns one case from READER's file.  Returns a null
631    pointer on failure. */
632 static struct ccase *
633 gnm_file_casereader_read (struct casereader *reader UNUSED, void *r_)
634 {
635   struct ccase *c;
636   int ret = 0;
637
638   struct gnumeric_reader *r = r_;
639   int current_row = r->row;
640
641   if ( !r->used_first_case )
642     {
643       r->used_first_case = true;
644       return r->first_case;
645     }
646
647   c = case_create (r->proto);
648   case_set_missing (c);
649
650   if (r->start_col == -1)
651     r->start_col = r->min_col;
652
653   while ((r->state == STATE_CELL || r->state == STATE_CELLS_START )
654          && r->row == current_row && (ret = xmlTextReaderRead (r->xtr)))
655     {
656       process_node (r);
657
658       if ( r->col < r->start_col || (r->stop_col != -1 &&
659                                      r->col > r->stop_col))
660         continue;
661
662       if ( r->col - r->start_col >= caseproto_get_n_widths (r->proto))
663         continue;
664
665       if ( r->stop_row != -1 && r->row > r->stop_row)
666         break;
667
668       if ( r->node_type == XML_READER_TYPE_TEXT )
669         {
670           xmlChar *value = xmlTextReaderValue (r->xtr);
671
672           const int idx = r->col - r->start_col;
673
674           const struct variable *var = dict_get_var (r->dict, idx);
675
676           convert_xml_string_to_value (c, var, value);
677
678           free (value);
679         }
680
681     }
682
683   if (ret == 1)
684     return c;
685   else
686     {
687       case_unref (c);
688       return NULL;
689     }
690 }
691
692
693 #endif /* GNM_SUPPORT */