Started polymorphism of ODS
[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.sheets = sheet_count;
408
409   ds_destroy (&errs);
410
411   return &r->spreadsheet;
412
413  error:
414   zip_reader_destroy (zreader);
415   ds_destroy (&errs);
416   return NULL;
417 }
418
419 struct casereader *
420 ods_make_reader (struct spreadsheet *spreadsheet, 
421                  const struct spreadsheet_read_info *gri, struct spreadsheet_read_options *opts)
422 {
423   intf ret = 0;
424   xmlChar *type = NULL;
425   unsigned long int vstart = 0;
426   casenumber n_cases = CASENUMBER_MAX;
427   int i;
428   struct var_spec *var_spec = NULL;
429   int n_var_specs = 0;
430
431   struct ods_reader *r = (struct ods_reader *) spreadsheet;
432   xmlChar *val_string = NULL;
433
434   assert (r);
435   r->read_names = gri->read_names;
436   ds_init_empty (&r->ods_errs);
437
438   if ( opts->cell_range )
439     {
440       if ( ! convert_cell_ref (opts->cell_range,
441                                &r->start_col, &r->start_row,
442                                &r->stop_col, &r->stop_row))
443         {
444           msg (SE, _("Invalid cell range `%s'"),
445                opts->cell_range);
446           goto error;
447         }
448     }
449   else
450     {
451       r->start_col = 0;
452       r->start_row = 0;
453       r->stop_col = -1;
454       r->stop_row = -1;
455     }
456
457   r->state = STATE_INIT;
458   r->target_sheet = BAD_CAST opts->sheet_name;
459   r->target_sheet_index = opts->sheet_index;
460   r->row = r->col = -1;
461   r->sheet_index = 0;
462
463
464   /* If CELLRANGE was given, then we know how many variables should be read */
465   if ( r->stop_col != -1 )
466     {
467       assert (var_spec == NULL);
468       n_var_specs =  r->stop_col - r->start_col + 1;
469       var_spec = xrealloc (var_spec, sizeof (*var_spec) * n_var_specs);
470       memset (var_spec, '\0', sizeof (*var_spec) * n_var_specs);
471     }
472
473
474   /* Advance to the start of the cells for the target sheet */
475   while ( (r->row < r->start_row ))
476     {
477       if (1 != (ret = xmlTextReaderRead (r->xtr)))
478            break;
479
480       process_node (r);
481     }
482
483   if (ret < 1)
484     {
485       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
486            gri->file_name);
487       goto error;
488     }
489
490   if ( gri->read_names)
491     {
492       while (1 == (ret = xmlTextReaderRead (r->xtr)))
493         {
494           int idx;
495           process_node (r);
496           if ( r->row > r->start_row)
497             break;
498
499           if (r->col == -1 && r->row == r->start_row)
500             break;
501
502           if ( r->col < r->start_col)
503             continue;
504
505           idx = r->col - r->start_col;
506
507           if (r->state == STATE_CELL_CONTENT 
508               &&
509               XML_READER_TYPE_TEXT  == r->node_type)
510             {
511               xmlChar *value = xmlTextReaderValue (r->xtr);
512               if ( idx >= n_var_specs)
513                 {
514
515                   var_spec = xrealloc (var_spec, sizeof (*var_spec) * (idx + 1));
516
517                   /* xrealloc (unlike realloc) doesn't initialise its memory to 0 */
518                   memset (var_spec + n_var_specs,
519                           0, 
520                           (n_var_specs - idx + 1) * sizeof (*var_spec));
521                   n_var_specs = idx + 1;
522                 }
523               var_spec[idx].firstval.text = 0;
524               var_spec[idx].firstval.value = 0;
525               var_spec[idx].firstval.type = 0;
526
527               var_spec [idx].name = strdup (CHAR_CAST (const char *, value));
528               free (value);
529               value = NULL;
530             }
531         }
532     }
533
534   /* Read in the first row of data */
535   while (1 == xmlTextReaderRead (r->xtr))
536     {
537       int idx;
538       process_node (r);
539       if ( r->row >= r->start_row + 1 + gri->read_names)
540         break;
541
542       if ( r->col < r->start_col)
543         continue;
544
545       if ( r->col - r->start_col + 1 > n_var_specs)
546         continue;
547
548       idx = r->col - r->start_col;
549
550       if ( r->state == STATE_CELL &&
551            XML_READER_TYPE_ELEMENT  == r->node_type)
552         {
553           type = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value-type"));
554           val_string = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value"));
555         }
556
557       if ( r->state == STATE_CELL_CONTENT &&
558            XML_READER_TYPE_TEXT  == r->node_type)
559         {
560           var_spec [idx].firstval.type = type;
561           var_spec [idx].firstval.text = xmlTextReaderValue (r->xtr);
562           var_spec [idx].firstval.value = val_string;
563           val_string = NULL;
564           type = NULL;
565         }
566     }
567
568   /* Create the dictionary and populate it */
569   r->spreadsheet.dict = r->dict = dict_create (
570     CHAR_CAST (const char *, xmlTextReaderConstEncoding (r->xtr)));
571
572   for (i = 0 ; i < n_var_specs ; ++i )
573     {
574       struct fmt_spec fmt;
575       struct variable *var = NULL;
576       char *name = dict_make_unique_var_name (r->dict, var_spec[i].name, &vstart);
577       int width  = xmv_to_width (&var_spec[i].firstval, gri->asw);
578       dict_create_var (r->dict, name, width);
579       free (name);
580
581       var = dict_get_var (r->dict, i);
582
583       if ( 0 == xmlStrcmp (var_spec[i].firstval.type, _xml("date")))
584         {
585           fmt.type = FMT_DATE;
586           fmt.d = 0;
587           fmt.w = 20;
588         }
589       else
590         fmt = fmt_default_for_width (width);
591
592       var_set_both_formats (var, &fmt);
593     }
594
595   /* Create the first case, and cache it */
596   r->used_first_case = false;
597
598   if ( n_var_specs ==  0 )
599     {
600       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
601            gri->file_name);
602       goto error;
603     }
604
605   r->proto = caseproto_ref (dict_get_proto (r->dict));
606   r->first_case = case_create (r->proto);
607   case_set_missing (r->first_case);
608
609   for ( i = 0 ; i < n_var_specs ; ++i )
610     {
611       const struct variable *var = dict_get_var (r->dict, i);
612
613       convert_xml_to_value (r->first_case, var,  &var_spec[i].firstval);
614     }
615
616   //  zip_reader_destroy (zreader);
617
618   for ( i = 0 ; i < n_var_specs ; ++i )
619     {
620       free (var_spec[i].firstval.type);
621       free (var_spec[i].firstval.value);
622       free (var_spec[i].firstval.text);
623       free (var_spec[i].name);
624     }
625
626   free (var_spec);
627
628   return casereader_create_sequential
629     (NULL,
630      r->proto,
631      n_cases,
632      &ods_file_casereader_class, r);
633
634  error:
635   
636   // zip_reader_destroy (zreader);
637
638   for ( i = 0 ; i < n_var_specs ; ++i )
639     {
640       free (var_spec[i].firstval.type);
641       free (var_spec[i].firstval.value);
642       free (var_spec[i].firstval.text);
643       free (var_spec[i].name);
644     }
645
646   free (var_spec);
647
648   dict_destroy (r->spreadsheet.dict);
649   r->spreadsheet.dict = NULL;
650   ods_file_casereader_destroy (NULL, r);
651
652
653   return NULL;
654 }
655
656
657 /* Reads and returns one case from READER's file.  Returns a null
658    pointer on failure. */
659 static struct ccase *
660 ods_file_casereader_read (struct casereader *reader UNUSED, void *r_)
661 {
662   struct ccase *c = NULL;
663   xmlChar *val_string = NULL;
664   struct ods_reader *r = r_;
665   int current_row = r->row;
666
667   if ( r->row == -1)
668     return NULL;
669
670   if ( !r->used_first_case )
671     {
672       r->used_first_case = true;
673       return r->first_case;
674     }
675
676   if ( r->state > STATE_INIT)
677     {
678       c = case_create (r->proto);
679       case_set_missing (c);
680     }
681
682   while (1 == xmlTextReaderRead (r->xtr))
683     {
684       process_node (r);
685       if ( r->row > current_row)
686         {
687           break;
688         }
689       if ( r->col < r->start_col || (r->stop_col != -1 && r->col > r->stop_col))
690         {
691           continue;
692         }
693       if ( r->col - r->start_col >= caseproto_get_n_widths (r->proto))
694         {
695           continue;
696         }
697       if ( r->stop_row != -1 && r->row > r->stop_row)
698         {
699           continue;
700         }
701       if ( r->state == STATE_CELL &&
702            r->node_type == XML_READER_TYPE_ELEMENT )
703         {
704           val_string = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value"));
705         }
706
707       if ( r->state == STATE_CELL_CONTENT && r->node_type == XML_READER_TYPE_TEXT )
708         {
709           int col;
710           struct xml_value *xmv = xzalloc (sizeof *xmv);
711           xmv->text = xmlTextReaderValue (r->xtr);
712           xmv->value = val_string;
713           val_string = NULL;
714
715           for (col = 0; col < r->span ; ++col)
716             {
717               const int idx = r->col + col - r->start_col;
718
719               const struct variable *var = dict_get_var (r->dict, idx);
720
721               convert_xml_to_value (c, var, xmv);
722             }
723           free (xmv->text);
724           free (xmv->value);
725           free (xmv);
726         }
727
728       if ( r->state < STATE_TABLE)
729         break;
730     }
731
732   if (NULL == c || (r->stop_row != -1 && r->row > r->stop_row + 1))
733     {
734       case_unref (c);
735       return NULL;
736     }
737   else
738     {
739       return c;
740     }
741 }
742 #endif