Made a start at canonicalising the interface
[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   /* The total number of sheets in the "workbook" */
99   int sheet_total ;
100
101   int row;
102   int col;
103   int min_col;
104   int node_type;
105   int sheet_index;
106
107
108   const xmlChar *target_sheet;
109   int target_sheet_index;
110
111   int start_row;
112   int start_col;
113   int stop_row;
114   int stop_col;
115
116   struct caseproto *proto;
117   struct dictionary *dict;
118   struct ccase *first_case;
119   bool used_first_case;
120 };
121
122 static void process_node (struct gnumeric_reader *r);
123
124
125 static void
126 gnm_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
127 {
128   struct gnumeric_reader *r = r_;
129   if ( r == NULL)
130         return ;
131
132   if ( r->xtr)
133     xmlFreeTextReader (r->xtr);
134
135   if ( ! r->used_first_case )
136     case_unref (r->first_case);
137
138   caseproto_unref (r->proto);
139
140   free (r);
141 }
142
143 static void
144 process_node (struct gnumeric_reader *r)
145 {
146   xmlChar *name = xmlTextReaderName (r->xtr);
147   if (name == NULL)
148     name = xmlStrdup (_xml ("--"));
149
150
151   r->node_type = xmlTextReaderNodeType (r->xtr);
152
153   switch ( r->state)
154     {
155     case STATE_PRE_INIT:
156       if (0 == xmlStrcasecmp (name, _xml("gnm:SheetNameIndex")) &&
157           XML_READER_TYPE_ELEMENT  == r->node_type)
158         {
159           r->state = STATE_SHEET_COUNT;
160           r->sheet_total = 0;
161         }
162       break;
163
164     case STATE_SHEET_COUNT:
165       if (0 == xmlStrcasecmp (name, _xml("gnm:SheetName")) &&
166           XML_READER_TYPE_ELEMENT  == r->node_type)
167         {
168           r->sheet_total++;
169         }
170       else if (0 == xmlStrcasecmp (name, _xml("gnm:SheetNameIndex")) &&
171           XML_READER_TYPE_END_ELEMENT  == r->node_type)
172         {
173           r->state = STATE_INIT;
174         }
175       break;
176
177     case STATE_INIT:
178       if (0 == xmlStrcasecmp (name, _xml("gnm:Sheet")) &&
179           XML_READER_TYPE_ELEMENT  == r->node_type)
180         {
181           ++r->sheet_index;
182           r->state = STATE_SHEET_START;
183         }
184       break;
185     case STATE_SHEET_START:
186       if (0 == xmlStrcasecmp (name, _xml("gnm:Name"))  &&
187           XML_READER_TYPE_ELEMENT  == r->node_type)
188         {
189           r->state = STATE_SHEET_NAME;
190         }
191       break;
192     case STATE_SHEET_NAME:
193       if (0 == xmlStrcasecmp (name, _xml("gnm:Name"))  &&
194           XML_READER_TYPE_END_ELEMENT  == r->node_type)
195         {
196           r->state = STATE_INIT;
197         }
198       else if (XML_READER_TYPE_TEXT == r->node_type)
199         {
200           if ( r->target_sheet != NULL)
201             {
202               xmlChar *value = xmlTextReaderValue (r->xtr);
203               if ( 0 == xmlStrcmp (value, r->target_sheet))
204                 r->state = STATE_SHEET_FOUND;
205               free (value);
206             }
207           else if (r->target_sheet_index == r->sheet_index)
208             {
209               r->state = STATE_SHEET_FOUND;
210             }
211         }
212       break;
213     case STATE_SHEET_FOUND:
214       if (0 == xmlStrcasecmp (name, _xml("gnm:Cells"))  &&
215           XML_READER_TYPE_ELEMENT  == r->node_type)
216         {
217           r->min_col = INT_MAX;
218           if (! xmlTextReaderIsEmptyElement (r->xtr))
219             r->state = STATE_CELLS_START;
220         }
221       else if (0 == xmlStrcasecmp (name, _xml("gnm:MaxRow"))  &&
222           XML_READER_TYPE_ELEMENT  == r->node_type)
223         {
224           r->state = STATE_MAXROW;
225         }
226       else if (0 == xmlStrcasecmp (name, _xml("gnm:Sheet"))  &&
227           XML_READER_TYPE_END_ELEMENT  == r->node_type)
228         {
229           r->state = STATE_INIT;
230         }
231       break;
232     case STATE_MAXROW:
233       if (0 == xmlStrcasecmp (name, _xml("gnm:MaxRow"))  &&
234           XML_READER_TYPE_END_ELEMENT  == r->node_type)
235         {
236           r->state = STATE_SHEET_FOUND;
237         }
238     case STATE_CELLS_START:
239       if (0 == xmlStrcasecmp (name, _xml ("gnm:Cell"))  &&
240           XML_READER_TYPE_ELEMENT  == r->node_type)
241         {
242           xmlChar *attr = NULL;
243           r->state = STATE_CELL;
244
245           attr = xmlTextReaderGetAttribute (r->xtr, _xml ("Col"));
246           r->col =  _xmlchar_to_int (attr);
247           free (attr);
248
249           if (r->col < r->min_col)
250             r->min_col = r->col;
251
252           attr = xmlTextReaderGetAttribute (r->xtr, _xml ("Row"));
253           r->row = _xmlchar_to_int (attr);
254           free (attr);
255         }
256       else if (0 == xmlStrcasecmp (name, _xml("gnm:Cells"))  &&
257                XML_READER_TYPE_END_ELEMENT  == r->node_type)
258         r->state = STATE_SHEET_NAME;
259
260       break;
261     case STATE_CELL:
262       if (0 == xmlStrcasecmp (name, _xml("gnm:Cell"))  &&
263                               XML_READER_TYPE_END_ELEMENT  == r->node_type)
264         {
265           r->state = STATE_CELLS_START;
266         }
267       break;
268     default:
269       break;
270     };
271
272   xmlFree (name);
273 }
274
275
276 /*
277    Sets the VAR of case C, to the value corresponding to the xml string XV
278  */
279 static void
280 convert_xml_string_to_value (struct ccase *c, const struct variable *var,
281                              const xmlChar *xv)
282 {
283   union value *v = case_data_rw (c, var);
284
285   if (xv == NULL)
286     value_set_missing (v, var_get_width (var));
287   else if ( var_is_alpha (var))
288     value_copy_str_rpad (v, var_get_width (var), xv, ' ');
289   else
290     {
291       const char *text = CHAR_CAST (const char *, xv);
292       char *endptr;
293
294       errno = 0;
295       v->f = c_strtod (text, &endptr);
296       if ( errno != 0 || endptr == text)
297         v->f = SYSMIS;
298     }
299 }
300
301 struct var_spec
302 {
303   char *name;
304   int width;
305   xmlChar *first_value;
306 };
307
308 struct spreadsheet *
309 gnumeric_probe (const char *filename)
310 {
311   int ret;
312   struct gnumeric_reader *r = NULL;
313   xmlTextReaderPtr xtr;
314
315   gzFile gz = gzopen (filename, "r");
316
317   if (NULL == gz)
318     return NULL;
319
320   xtr = xmlReaderForIO ((xmlInputReadCallback) gzread,
321                            (xmlInputCloseCallback) gzclose, gz,
322                            NULL, NULL, 0);
323
324   if (xtr == NULL)
325     return NULL;
326
327   r = xzalloc (sizeof *r);
328   
329   r->xtr = xtr;
330   r->sheet_total = -1;
331   r->state = STATE_PRE_INIT;
332
333
334   /* Advance to the start of the workbook.
335      This gives us some confidence that we are actually dealing with a gnumeric
336      spreadsheet.
337    */
338   while ( (r->state != STATE_INIT )
339           && 1 == (ret = xmlTextReaderRead (r->xtr)))
340     {
341       process_node (r);
342     }
343
344   if (ret != 1)
345     {
346       /* Not a gnumeric spreadsheet */
347       free (r);
348       gzclose (gz);
349       return NULL;
350     }
351     
352   r->spreadsheet.type = SPREADSHEET_GNUMERIC;
353   r->spreadsheet.sheets = r->sheet_total;
354   r->spreadsheet.make_reader = NULL;
355   
356   
357   return &r->spreadsheet;
358 }
359
360 struct casereader *
361 gnumeric_open_reader (const struct spreadsheet_read_info *gri, 
362                       struct spreadsheet_read_options *opts,
363                       struct dictionary **dict)
364 {
365   unsigned long int vstart = 0;
366   int ret;
367   casenumber n_cases = CASENUMBER_MAX;
368   int i;
369   struct var_spec *var_spec = NULL;
370   int n_var_specs = 0;
371
372   struct spreadsheet * spreadsheet = NULL;
373   struct gnumeric_reader *r = NULL;
374
375   spreadsheet = gnumeric_probe (gri->file_name);
376
377   if (spreadsheet == NULL)
378     goto error;
379
380   r = (struct gnumeric_reader *) (spreadsheet);
381
382   if ( opts->cell_range )
383     {
384       if ( ! convert_cell_ref (opts->cell_range,
385                                &r->start_col, &r->start_row,
386                                &r->stop_col, &r->stop_row))
387         {
388           msg (SE, _("Invalid cell range `%s'"),
389                opts->cell_range);
390           goto error;
391         }
392     }
393   else
394     {
395       r->start_col = -1;
396       r->start_row = 0;
397       r->stop_col = -1;
398       r->stop_row = -1;
399     }
400
401
402   r->target_sheet = BAD_CAST opts->sheet_name;
403   r->target_sheet_index = opts->sheet_index;
404   r->row = r->col = -1;
405   r->sheet_index = 0;
406
407   /* Advance to the start of the cells for the target sheet */
408   while ( (r->state != STATE_CELL || r->row < r->start_row )
409           && 1 == (ret = xmlTextReaderRead (r->xtr)))
410     {
411       xmlChar *value ;
412       process_node (r);
413       value = xmlTextReaderValue (r->xtr);
414
415       if ( r->state == STATE_MAXROW  && r->node_type == XML_READER_TYPE_TEXT)
416         {
417           n_cases = 1 + _xmlchar_to_int (value) ;
418         }
419       free (value);
420     }
421
422
423   /* If a range has been given, then  use that to calculate the number
424      of cases */
425   if ( opts->cell_range)
426     {
427       n_cases = MIN (n_cases, r->stop_row - r->start_row + 1);
428     }
429
430   if ( gri->read_names )
431     {
432       r->start_row++;
433       n_cases --;
434     }
435
436   /* Read in the first row of cells,
437      including the headers if read_names was set */
438   while (
439          (( r->state == STATE_CELLS_START && r->row <= r->start_row) || r->state == STATE_CELL )
440          && (ret = xmlTextReaderRead (r->xtr))
441          )
442     {
443       int idx;
444       process_node (r);
445
446       if ( r->row > r->start_row ) break;
447
448       if ( r->col < r->start_col ||
449            (r->stop_col != -1 && r->col > r->stop_col))
450         continue;
451
452       idx = r->col - r->start_col;
453
454       if ( idx  >= n_var_specs )
455         {
456           int i;
457           var_spec = xrealloc (var_spec, sizeof (*var_spec) * (idx + 1));
458           for (i = n_var_specs; i <= idx; ++i)
459           {
460             var_spec [i].name = NULL;
461             var_spec [i].width = -1;
462             var_spec [i].first_value = NULL;
463           }
464           n_var_specs =  idx + 1 ;
465         }
466
467       if ( r->node_type == XML_READER_TYPE_TEXT )
468         {
469           xmlChar *value = xmlTextReaderValue (r->xtr);
470           const char *text  = CHAR_CAST (const char *, value);
471
472           if ( r->row < r->start_row)
473             {
474               if ( gri->read_names )
475                 {
476                   var_spec [idx].name = xstrdup (text);
477                 }
478             }
479           else
480             {
481               var_spec [idx].first_value = xmlStrdup (value);
482
483               if (-1 ==  var_spec [idx].width )
484                 var_spec [idx].width = (gri->asw == -1) ?
485                   ROUND_UP (strlen(text), SPREADSHEET_DEFAULT_WIDTH) : gri->asw;
486             }
487
488           free (value);
489         }
490       else if ( r->node_type == XML_READER_TYPE_ELEMENT
491                 && r->state == STATE_CELL)
492         {
493           if ( r->row == r->start_row )
494             {
495               xmlChar *attr =
496                 xmlTextReaderGetAttribute (r->xtr, _xml ("ValueType"));
497
498               if ( NULL == attr || 60 !=  _xmlchar_to_int (attr))
499                 var_spec [idx].width = 0;
500
501               free (attr);
502             }
503         }
504     }
505
506   {
507     const xmlChar *enc = xmlTextReaderConstEncoding (r->xtr);
508     if ( enc == NULL)
509       goto error;
510     /* Create the dictionary and populate it */
511     *dict = r->dict = dict_create (CHAR_CAST (const char *, enc));
512   }
513
514   for (i = 0 ; i < n_var_specs ; ++i )
515     {
516       char *name;
517
518       if ( (var_spec[i].name == NULL) && (var_spec[i].first_value == NULL))
519         continue;
520
521       /* Probably no data exists for this variable, so allocate a
522          default width */
523       if ( var_spec[i].width == -1 )
524         var_spec[i].width = SPREADSHEET_DEFAULT_WIDTH;
525
526       name = dict_make_unique_var_name (r->dict, var_spec[i].name, &vstart);
527       dict_create_var (r->dict, name, var_spec[i].width);
528       free (name);
529     }
530
531   /* Create the first case, and cache it */
532   r->used_first_case = false;
533
534   if ( n_var_specs ==  0 )
535     {
536       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
537            gri->file_name);
538       goto error;
539     }
540
541   r->proto = caseproto_ref (dict_get_proto (r->dict));
542   r->first_case = case_create (r->proto);
543   case_set_missing (r->first_case);
544
545   int x = 0;
546   for ( i = 0 ; i < n_var_specs ; ++i )
547     {
548       if ( (var_spec[i].name == NULL) && (var_spec[i].first_value == NULL))
549         continue;
550
551       const struct variable *var = dict_get_var (r->dict, x++);
552
553       convert_xml_string_to_value (r->first_case, var,
554                                    var_spec[i].first_value);
555     }
556
557   for ( i = 0 ; i < n_var_specs ; ++i )
558     {
559       free (var_spec[i].first_value);
560       free (var_spec[i].name);
561     }
562
563   free (var_spec);
564   
565   
566   if (opts->cell_range == NULL)
567     {
568       opts->cell_range = c_xasprintf ("%c%d:%c%d", 
569                                        r->start_col + 'A',
570                                        r->start_row,
571                                        r->stop_col + 'A' + caseproto_get_n_widths (r->proto),
572                                        r->start_row + n_cases);
573     }
574   
575   return casereader_create_sequential
576     (NULL,
577      r->proto,
578      n_cases,
579      &gnm_file_casereader_class, r);
580
581
582  error:
583   for ( i = 0 ; i < n_var_specs ; ++i )
584     {
585       free (var_spec[i].first_value);
586       free (var_spec[i].name);
587     }
588
589   free (var_spec);
590   dict_destroy (*dict);
591   *dict = NULL;
592
593   gnm_file_casereader_destroy (NULL, r);
594
595   return NULL;
596 };
597
598
599 /* Reads and returns one case from READER's file.  Returns a null
600    pointer on failure. */
601 static struct ccase *
602 gnm_file_casereader_read (struct casereader *reader UNUSED, void *r_)
603 {
604   struct ccase *c;
605   int ret = 0;
606
607   struct gnumeric_reader *r = r_;
608   int current_row = r->row;
609
610   if ( !r->used_first_case )
611     {
612       r->used_first_case = true;
613       return r->first_case;
614     }
615
616   c = case_create (r->proto);
617   case_set_missing (c);
618
619   if (r->start_col == -1)
620     r->start_col = r->min_col;
621
622   while ((r->state == STATE_CELL || r->state == STATE_CELLS_START )
623          && r->row == current_row && (ret = xmlTextReaderRead (r->xtr)))
624     {
625       process_node (r);
626
627       if ( r->col < r->start_col || (r->stop_col != -1 &&
628                                      r->col > r->stop_col))
629         continue;
630
631       if ( r->col - r->start_col >= caseproto_get_n_widths (r->proto))
632         continue;
633
634       if ( r->stop_row != -1 && r->row > r->stop_row)
635         break;
636
637       if ( r->node_type == XML_READER_TYPE_TEXT )
638         {
639           xmlChar *value = xmlTextReaderValue (r->xtr);
640
641           const int idx = r->col - r->start_col;
642
643           const struct variable *var = dict_get_var (r->dict, idx);
644
645           convert_xml_string_to_value (c, var, value);
646
647           free (value);
648         }
649
650     }
651
652   if (ret == 1)
653     return c;
654   else
655     {
656       case_unref (c);
657       return NULL;
658     }
659 }
660
661
662 #endif /* GNM_SUPPORT */