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