Spreadsheet readers: Seperate the mandatory from the optional arguments.
[pspp] / src / data / ods-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 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 "data/data-in.h"
23
24 #include "gl/minmax.h"
25
26 #include "gettext.h"
27 #define _(msgid) gettext (msgid)
28 #define N_(msgid) (msgid)
29
30 #include "ods-reader.h"
31 #include "spreadsheet-reader.h"
32
33 #if !ODF_READ_SUPPORT
34
35 struct casereader *
36 ods_open_reader (const struct spreadsheet_read_info *gri, struct spreadsheet_read_options *opts, 
37                  struct dictionary **dict)
38 {
39   msg (ME, _("Support for %s files was not compiled into this installation of PSPP"), "OpenDocument");
40
41   return NULL;
42 }
43
44 #else
45
46 #include "libpspp/zip-reader.h"
47
48
49 #include <assert.h>
50 #include <stdbool.h>
51 #include <errno.h>
52 #include <libxml/xmlreader.h>
53 #include <zlib.h>
54
55 #include "data/format.h"
56 #include "data/case.h"
57 #include "data/casereader-provider.h"
58 #include "data/dictionary.h"
59 #include "data/identifier.h"
60 #include "data/value.h"
61 #include "data/variable.h"
62 #include "libpspp/i18n.h"
63 #include "libpspp/str.h"
64
65 #include "gl/xalloc.h"
66
67 static void ods_file_casereader_destroy (struct casereader *, void *);
68
69 static struct ccase *ods_file_casereader_read (struct casereader *, void *);
70
71 static const struct casereader_class ods_file_casereader_class =
72   {
73     ods_file_casereader_read,
74     ods_file_casereader_destroy,
75     NULL,
76     NULL,
77   };
78
79 enum reader_state
80   {
81     STATE_INIT = 0,        /* Initial state */
82     STATE_SPREADSHEET,     /* Found the start of the spreadsheet doc */
83     STATE_TABLE,           /* Found the sheet that we actually want */
84     STATE_ROW,             /* Found the start of the cell array */
85     STATE_CELL,            /* Found a cell */
86     STATE_CELL_CONTENT     /* Found a the text within a cell */
87   };
88
89 struct ods_reader
90 {
91   xmlTextReaderPtr xtr;
92
93   enum reader_state state;
94   bool sheet_found;
95   int row;
96   int col;
97   int node_type;
98   int sheet_index;
99
100   const xmlChar *target_sheet;
101   int target_sheet_index;
102
103   int start_row;
104   int start_col;
105   int stop_row;
106   int stop_col;
107
108   struct caseproto *proto;
109   struct dictionary *dict;
110   struct ccase *first_case;
111   bool used_first_case;
112   bool read_names;
113
114   struct string ods_errs;
115   int span;
116 };
117
118 static void process_node (struct ods_reader *r);
119
120 static void
121 ods_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
122 {
123   struct ods_reader *r = r_;
124   if ( r == NULL)
125     return ;
126
127   if (r->xtr)
128     xmlFreeTextReader (r->xtr);
129
130   if ( ! ds_is_empty (&r->ods_errs))
131     msg (ME, "%s", ds_cstr (&r->ods_errs));
132
133   ds_destroy (&r->ods_errs);
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 ods_reader *r)
145 {
146   xmlChar *name = xmlTextReaderName (r->xtr);
147   if (name == NULL)
148     name = xmlStrdup (_xml ("--"));
149
150   r->node_type = xmlTextReaderNodeType (r->xtr);
151
152   switch ( r->state)
153     {
154     case STATE_INIT:
155       if (0 == xmlStrcasecmp (name, _xml("office:spreadsheet")) &&
156           XML_READER_TYPE_ELEMENT  == r->node_type)
157         {
158           r->state = STATE_SPREADSHEET;
159         }
160       break;
161     case STATE_SPREADSHEET:
162       if (0 == xmlStrcasecmp (name, _xml("table:table")))
163         {
164           if (XML_READER_TYPE_ELEMENT == r->node_type)
165             {
166               r->col = -1;
167               r->row = -1;
168               ++r->sheet_index;
169               if ( r->target_sheet != NULL)
170                 {
171                   xmlChar *value = xmlTextReaderGetAttribute (r->xtr, _xml ("table:name"));
172                   if ( 0 == xmlStrcmp (value, r->target_sheet))
173                     {
174                       r->sheet_found = true;
175                       r->state = STATE_TABLE;
176                     }
177                   free (value);
178                 }
179               else if (r->target_sheet_index == r->sheet_index)
180                 {
181                   r->sheet_found = true;
182                   r->state = STATE_TABLE;
183                 }
184               else if ( r->target_sheet_index == -1)
185                 r->state = STATE_TABLE;
186             }
187         }
188       else if (XML_READER_TYPE_END_ELEMENT  == r->node_type
189                    && r->sheet_found)
190         {
191           r->state = STATE_INIT;
192         }
193         break;
194     case STATE_TABLE:
195       if (0 == xmlStrcasecmp (name, _xml("table:table-row")) )
196         {
197           if ( XML_READER_TYPE_ELEMENT  == r->node_type)
198             {
199               if (! xmlTextReaderIsEmptyElement (r->xtr))
200                 {
201                   r->state = STATE_ROW;
202                 }
203               r->row++;
204               r->span = 1;
205             }
206         }
207       else if (XML_READER_TYPE_END_ELEMENT  == r->node_type)
208         {
209           r->state = STATE_SPREADSHEET;
210         }
211       break;
212     case STATE_ROW:
213       if (0 == xmlStrcasecmp (name, _xml ("table:table-cell")))
214         {
215           if ( XML_READER_TYPE_ELEMENT  == r->node_type)
216             {
217               xmlChar *value =
218                 xmlTextReaderGetAttribute (r->xtr,
219                                            _xml ("table:number-columns-repeated"));
220               r->col += r->span;
221               r->span = value ? _xmlchar_to_int (value) : 1;
222               free (value);
223               if (! xmlTextReaderIsEmptyElement (r->xtr))
224                 {
225                   r->state = STATE_CELL;
226                 }
227             }
228         }
229       else if (XML_READER_TYPE_END_ELEMENT  == r->node_type)
230         {
231           r->state = STATE_TABLE;
232           r->col = -1;
233           /* Set the span back to the default */
234           r->span = 1;
235         }
236       break;
237     case STATE_CELL:
238       if (0 == xmlStrcasecmp (name, _xml("text:p")))
239         {
240           if ( XML_READER_TYPE_ELEMENT  == r->node_type)
241             {
242               r->state = STATE_CELL_CONTENT;
243             }
244         }
245       else if (XML_READER_TYPE_END_ELEMENT  == r->node_type)
246         {
247           r->state = STATE_ROW;
248         }
249       break;
250     case STATE_CELL_CONTENT:
251       if (XML_READER_TYPE_TEXT != r->node_type)
252         r->state = STATE_CELL;
253       break;
254     default:
255       break;
256     };
257
258   xmlFree (name);
259 }
260
261 /* 
262    A struct containing the parameters of a cell's value 
263    parsed from the xml
264 */
265 struct xml_value
266 {
267   xmlChar *type;
268   xmlChar *value;
269   xmlChar *text;
270 };
271
272 struct var_spec
273 {
274   char *name;
275   struct xml_value firstval;
276 };
277
278
279 /* Determine the width that a xmv should probably have */
280 static int
281 xmv_to_width (const struct xml_value *xmv, int fallback)
282 {
283   int width = SPREADSHEET_DEFAULT_WIDTH;
284
285   /* Non-strings always have zero width */
286   if (xmv->type != NULL && 0 != xmlStrcmp (xmv->type, _xml("string")))
287     return 0;
288
289   if ( fallback != -1)
290     return fallback;
291
292   if ( xmv->value )
293     width = ROUND_UP (xmlStrlen (xmv->value),
294                       SPREADSHEET_DEFAULT_WIDTH);
295   else if ( xmv->text)
296     width = ROUND_UP (xmlStrlen (xmv->text),
297                       SPREADSHEET_DEFAULT_WIDTH);
298
299   return width;
300 }
301
302 /*
303    Sets the VAR of case C, to the value corresponding to the xml data
304  */
305 static void
306 convert_xml_to_value (struct ccase *c, const struct variable *var,
307                       const struct xml_value *xmv)
308 {
309   union value *v = case_data_rw (c, var);
310
311   if (xmv->value == NULL && xmv->text == NULL)
312     value_set_missing (v, var_get_width (var));
313   else if ( var_is_alpha (var))
314     /* Use the text field, because it seems that there is no
315        value field for strings */
316     value_copy_str_rpad (v, var_get_width (var), xmv->text, ' ');
317   else
318     {
319       const char *text ;
320       const struct fmt_spec *fmt = var_get_write_format (var);
321       enum fmt_category fc  = fmt_get_category (fmt->type);
322
323       assert ( fc != FMT_CAT_STRING);
324
325       text =
326         xmv->value ? CHAR_CAST (const char *, xmv->value) : CHAR_CAST (const char *, xmv->text);
327
328       free (data_in (ss_cstr (text), "UTF-8",
329                      fmt->type,
330                      v,
331                      var_get_width (var),
332                      "UTF-8"));
333     }
334 }
335
336
337 struct casereader *
338 ods_open_reader (const struct spreadsheet_read_info *gri, struct spreadsheet_read_options *opts,
339                  struct dictionary **dict)
340 {
341   int ret = 0;
342   xmlChar *type = NULL;
343   unsigned long int vstart = 0;
344   casenumber n_cases = CASENUMBER_MAX;
345   int i;
346   struct var_spec *var_spec = NULL;
347   int n_var_specs = 0;
348
349   struct ods_reader *r = xzalloc (sizeof *r);
350   struct zip_member *content = NULL;
351   struct zip_reader *zreader ;
352   xmlChar *val_string = NULL;
353
354   r->read_names = gri->read_names;
355   ds_init_empty (&r->ods_errs);
356
357   zreader = zip_reader_create (gri->file_name, &r->ods_errs);
358
359   if ( NULL == zreader)
360     {
361       msg (ME, _("Error opening `%s' for reading as a OpenDocument spreadsheet file: %s."),
362            gri->file_name, ds_cstr (&r->ods_errs));
363
364       goto error;
365     }
366
367   content = zip_member_open (zreader, "content.xml");
368   if ( NULL == content)
369     {
370       msg (ME, _("Could not extract OpenDocument spreadsheet from file `%s': %s."),
371            gri->file_name, ds_cstr (&r->ods_errs));
372
373       goto error;
374     }
375
376   zip_member_ref (content);
377
378   r->xtr = xmlReaderForIO ((xmlInputReadCallback) zip_member_read,
379                            (xmlInputCloseCallback) zip_member_finish,
380                            content,   NULL, NULL, XML_PARSE_RECOVER);
381
382   if ( r->xtr == NULL)
383     {
384       goto error;
385     }
386
387   if ( opts->cell_range )
388     {
389       if ( ! convert_cell_ref (opts->cell_range,
390                                &r->start_col, &r->start_row,
391                                &r->stop_col, &r->stop_row))
392         {
393           msg (SE, _("Invalid cell range `%s'"),
394                opts->cell_range);
395           goto error;
396         }
397     }
398   else
399     {
400       r->start_col = 0;
401       r->start_row = 0;
402       r->stop_col = -1;
403       r->stop_row = -1;
404     }
405
406   r->state = STATE_INIT;
407   r->target_sheet = BAD_CAST opts->sheet_name;
408   r->target_sheet_index = opts->sheet_index;
409   r->row = r->col = -1;
410   r->sheet_index = 0;
411
412
413   /* If CELLRANGE was given, then we know how many variables should be read */
414   if ( r->stop_col != -1 )
415     {
416       assert (var_spec == NULL);
417       n_var_specs =  r->stop_col - r->start_col + 1;
418       var_spec = xrealloc (var_spec, sizeof (*var_spec) * n_var_specs);
419       memset (var_spec, '\0', sizeof (*var_spec) * n_var_specs);
420     }
421
422
423   /* Advance to the start of the cells for the target sheet */
424   while ( (r->row < r->start_row ))
425     {
426       if (1 != (ret = xmlTextReaderRead (r->xtr)))
427            break;
428
429       process_node (r);
430     }
431
432   if (ret < 1)
433     {
434       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
435            gri->file_name);
436       goto error;
437     }
438
439   if ( gri->read_names)
440     {
441       while (1 == (ret = xmlTextReaderRead (r->xtr)))
442         {
443           int idx;
444           process_node (r);
445           if ( r->row > r->start_row)
446             break;
447
448           if (r->col == -1 && r->row == r->start_row)
449             break;
450
451           if ( r->col < r->start_col)
452             continue;
453
454           idx = r->col - r->start_col;
455
456           if (r->state == STATE_CELL_CONTENT 
457               &&
458               XML_READER_TYPE_TEXT  == r->node_type)
459             {
460               xmlChar *value = xmlTextReaderValue (r->xtr);
461               if ( idx >= n_var_specs)
462                 {
463
464                   var_spec = xrealloc (var_spec, sizeof (*var_spec) * (idx + 1));
465
466                   /* xrealloc (unlike realloc) doesn't initialise its memory to 0 */
467                   memset (var_spec + n_var_specs,
468                           0, 
469                           (n_var_specs - idx + 1) * sizeof (*var_spec));
470                   n_var_specs = idx + 1;
471                 }
472               var_spec[idx].firstval.text = 0;
473               var_spec[idx].firstval.value = 0;
474               var_spec[idx].firstval.type = 0;
475
476               var_spec [idx].name = strdup (CHAR_CAST (const char *, value));
477               free (value);
478               value = NULL;
479             }
480         }
481     }
482
483   /* Read in the first row of data */
484   while (1 == xmlTextReaderRead (r->xtr))
485     {
486       int idx;
487       process_node (r);
488       if ( r->row >= r->start_row + 1 + gri->read_names)
489         break;
490
491       if ( r->col < r->start_col)
492         continue;
493
494       if ( r->col - r->start_col + 1 > n_var_specs)
495         continue;
496
497       idx = r->col - r->start_col;
498
499       if ( r->state == STATE_CELL &&
500            XML_READER_TYPE_ELEMENT  == r->node_type)
501         {
502           type = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value-type"));
503           val_string = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value"));
504         }
505
506       if ( r->state == STATE_CELL_CONTENT &&
507            XML_READER_TYPE_TEXT  == r->node_type)
508         {
509           var_spec [idx].firstval.type = type;
510           var_spec [idx].firstval.text = xmlTextReaderValue (r->xtr);
511           var_spec [idx].firstval.value = val_string;
512           val_string = NULL;
513           type = NULL;
514         }
515     }
516
517   /* Create the dictionary and populate it */
518   *dict = r->dict = dict_create (
519     CHAR_CAST (const char *, xmlTextReaderConstEncoding (r->xtr)));
520
521   for (i = 0 ; i < n_var_specs ; ++i )
522     {
523       struct fmt_spec fmt;
524       struct variable *var = NULL;
525       char *name = dict_make_unique_var_name (r->dict, var_spec[i].name, &vstart);
526       int width  = xmv_to_width (&var_spec[i].firstval, gri->asw);
527       dict_create_var (r->dict, name, width);
528       free (name);
529
530       var = dict_get_var (r->dict, i);
531
532       if ( 0 == xmlStrcmp (var_spec[i].firstval.type, _xml("date")))
533         {
534           fmt.type = FMT_DATE;
535           fmt.d = 0;
536           fmt.w = 20;
537         }
538       else
539         fmt = fmt_default_for_width (width);
540
541       var_set_both_formats (var, &fmt);
542     }
543
544   /* Create the first case, and cache it */
545   r->used_first_case = false;
546
547   if ( n_var_specs ==  0 )
548     {
549       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
550            gri->file_name);
551       goto error;
552     }
553
554   r->proto = caseproto_ref (dict_get_proto (r->dict));
555   r->first_case = case_create (r->proto);
556   case_set_missing (r->first_case);
557
558   for ( i = 0 ; i < n_var_specs ; ++i )
559     {
560       const struct variable *var = dict_get_var (r->dict, i);
561
562       convert_xml_to_value (r->first_case, var,  &var_spec[i].firstval);
563     }
564
565   zip_reader_destroy (zreader);
566
567   for ( i = 0 ; i < n_var_specs ; ++i )
568     {
569       free (var_spec[i].firstval.type);
570       free (var_spec[i].firstval.value);
571       free (var_spec[i].firstval.text);
572       free (var_spec[i].name);
573     }
574
575   free (var_spec);
576
577   return casereader_create_sequential
578     (NULL,
579      r->proto,
580      n_cases,
581      &ods_file_casereader_class, r);
582
583  error:
584   
585   zip_reader_destroy (zreader);
586
587   for ( i = 0 ; i < n_var_specs ; ++i )
588     {
589       free (var_spec[i].firstval.type);
590       free (var_spec[i].firstval.value);
591       free (var_spec[i].firstval.text);
592       free (var_spec[i].name);
593     }
594
595   free (var_spec);
596
597   dict_destroy (r->dict);
598   ods_file_casereader_destroy (NULL, r);
599
600
601   return NULL;
602 }
603
604
605 /* Reads and returns one case from READER's file.  Returns a null
606    pointer on failure. */
607 static struct ccase *
608 ods_file_casereader_read (struct casereader *reader UNUSED, void *r_)
609 {
610   struct ccase *c = NULL;
611   xmlChar *val_string = NULL;
612   struct ods_reader *r = r_;
613   int current_row = r->row;
614
615   if ( r->row == -1)
616     return NULL;
617
618   if ( !r->used_first_case )
619     {
620       r->used_first_case = true;
621       return r->first_case;
622     }
623
624
625   if ( r->state > STATE_INIT)
626     {
627       c = case_create (r->proto);
628       case_set_missing (c);
629     }
630
631   while (1 == xmlTextReaderRead (r->xtr))
632     {
633       process_node (r);
634       if ( r->row > current_row)
635         {
636           break;
637         }
638       if ( r->col < r->start_col || (r->stop_col != -1 && r->col > r->stop_col))
639         {
640           continue;
641         }
642       if ( r->col - r->start_col >= caseproto_get_n_widths (r->proto))
643         {
644           continue;
645         }
646       if ( r->stop_row != -1 && r->row > r->stop_row)
647         {
648           continue;
649         }
650       if ( r->state == STATE_CELL &&
651            r->node_type == XML_READER_TYPE_ELEMENT )
652         {
653           val_string = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value"));
654         }
655
656       if ( r->state == STATE_CELL_CONTENT && r->node_type == XML_READER_TYPE_TEXT )
657         {
658           int col;
659           struct xml_value *xmv = xzalloc (sizeof *xmv);
660           xmv->text = xmlTextReaderValue (r->xtr);
661           xmv->value = val_string;
662           val_string = NULL;
663
664           for (col = 0; col < r->span ; ++col)
665             {
666               const int idx = r->col + col - r->start_col;
667
668               const struct variable *var = dict_get_var (r->dict, idx);
669
670               convert_xml_to_value (c, var, xmv);
671             }
672           free (xmv->text);
673           free (xmv->value);
674           free (xmv);
675         }
676
677       if ( r->state < STATE_TABLE)
678         break;
679     }
680
681   if (NULL == c || (r->stop_row != -1 && r->row > r->stop_row + 1))
682     {
683       case_unref (c);
684       return NULL;
685     }
686   else
687     {
688       return c;
689     }
690 }
691 #endif