85d3bc7dc10b47133ffe943fb0e408b5f419bfb7
[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 struct casereader *
340 ods_open_reader (const struct spreadsheet_read_info *gri, struct spreadsheet_read_options *opts,
341                  struct dictionary **dict)
342 {
343   int ret = 0;
344   xmlChar *type = NULL;
345   unsigned long int vstart = 0;
346   casenumber n_cases = CASENUMBER_MAX;
347   int i;
348   struct var_spec *var_spec = NULL;
349   int n_var_specs = 0;
350
351   struct ods_reader *r = xzalloc (sizeof *r);
352   struct zip_member *content = NULL;
353   struct zip_reader *zreader ;
354   xmlChar *val_string = NULL;
355
356   r->read_names = gri->read_names;
357   ds_init_empty (&r->ods_errs);
358
359   zreader = zip_reader_create (gri->file_name, &r->ods_errs);
360
361   if ( NULL == zreader)
362     {
363       msg (ME, _("Error opening `%s' for reading as a OpenDocument spreadsheet file: %s."),
364            gri->file_name, ds_cstr (&r->ods_errs));
365
366       goto error;
367     }
368
369   content = zip_member_open (zreader, "content.xml");
370   if ( NULL == content)
371     {
372       msg (ME, _("Could not extract OpenDocument spreadsheet from file `%s': %s."),
373            gri->file_name, ds_cstr (&r->ods_errs));
374
375       goto error;
376     }
377
378   zip_member_ref (content);
379
380   r->xtr = xmlReaderForIO ((xmlInputReadCallback) zip_member_read,
381                            (xmlInputCloseCallback) zip_member_finish,
382                            content,   NULL, NULL, XML_PARSE_RECOVER);
383
384   if ( r->xtr == NULL)
385     {
386       goto error;
387     }
388
389   if ( opts->cell_range )
390     {
391       if ( ! convert_cell_ref (opts->cell_range,
392                                &r->start_col, &r->start_row,
393                                &r->stop_col, &r->stop_row))
394         {
395           msg (SE, _("Invalid cell range `%s'"),
396                opts->cell_range);
397           goto error;
398         }
399     }
400   else
401     {
402       r->start_col = 0;
403       r->start_row = 0;
404       r->stop_col = -1;
405       r->stop_row = -1;
406     }
407
408   r->state = STATE_INIT;
409   r->target_sheet = BAD_CAST opts->sheet_name;
410   r->target_sheet_index = opts->sheet_index;
411   r->row = r->col = -1;
412   r->sheet_index = 0;
413
414
415   /* If CELLRANGE was given, then we know how many variables should be read */
416   if ( r->stop_col != -1 )
417     {
418       assert (var_spec == NULL);
419       n_var_specs =  r->stop_col - r->start_col + 1;
420       var_spec = xrealloc (var_spec, sizeof (*var_spec) * n_var_specs);
421       memset (var_spec, '\0', sizeof (*var_spec) * n_var_specs);
422     }
423
424
425   /* Advance to the start of the cells for the target sheet */
426   while ( (r->row < r->start_row ))
427     {
428       if (1 != (ret = xmlTextReaderRead (r->xtr)))
429            break;
430
431       process_node (r);
432     }
433
434   if (ret < 1)
435     {
436       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
437            gri->file_name);
438       goto error;
439     }
440
441   if ( gri->read_names)
442     {
443       while (1 == (ret = xmlTextReaderRead (r->xtr)))
444         {
445           int idx;
446           process_node (r);
447           if ( r->row > r->start_row)
448             break;
449
450           if (r->col == -1 && r->row == r->start_row)
451             break;
452
453           if ( r->col < r->start_col)
454             continue;
455
456           idx = r->col - r->start_col;
457
458           if (r->state == STATE_CELL_CONTENT 
459               &&
460               XML_READER_TYPE_TEXT  == r->node_type)
461             {
462               xmlChar *value = xmlTextReaderValue (r->xtr);
463               if ( idx >= n_var_specs)
464                 {
465
466                   var_spec = xrealloc (var_spec, sizeof (*var_spec) * (idx + 1));
467
468                   /* xrealloc (unlike realloc) doesn't initialise its memory to 0 */
469                   memset (var_spec + n_var_specs,
470                           0, 
471                           (n_var_specs - idx + 1) * sizeof (*var_spec));
472                   n_var_specs = idx + 1;
473                 }
474               var_spec[idx].firstval.text = 0;
475               var_spec[idx].firstval.value = 0;
476               var_spec[idx].firstval.type = 0;
477
478               var_spec [idx].name = strdup (CHAR_CAST (const char *, value));
479               free (value);
480               value = NULL;
481             }
482         }
483     }
484
485   /* Read in the first row of data */
486   while (1 == xmlTextReaderRead (r->xtr))
487     {
488       int idx;
489       process_node (r);
490       if ( r->row >= r->start_row + 1 + gri->read_names)
491         break;
492
493       if ( r->col < r->start_col)
494         continue;
495
496       if ( r->col - r->start_col + 1 > n_var_specs)
497         continue;
498
499       idx = r->col - r->start_col;
500
501       if ( r->state == STATE_CELL &&
502            XML_READER_TYPE_ELEMENT  == r->node_type)
503         {
504           type = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value-type"));
505           val_string = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value"));
506         }
507
508       if ( r->state == STATE_CELL_CONTENT &&
509            XML_READER_TYPE_TEXT  == r->node_type)
510         {
511           var_spec [idx].firstval.type = type;
512           var_spec [idx].firstval.text = xmlTextReaderValue (r->xtr);
513           var_spec [idx].firstval.value = val_string;
514           val_string = NULL;
515           type = NULL;
516         }
517     }
518
519   /* Create the dictionary and populate it */
520   *dict = r->dict = dict_create (
521     CHAR_CAST (const char *, xmlTextReaderConstEncoding (r->xtr)));
522
523   for (i = 0 ; i < n_var_specs ; ++i )
524     {
525       struct fmt_spec fmt;
526       struct variable *var = NULL;
527       char *name = dict_make_unique_var_name (r->dict, var_spec[i].name, &vstart);
528       int width  = xmv_to_width (&var_spec[i].firstval, gri->asw);
529       dict_create_var (r->dict, name, width);
530       free (name);
531
532       var = dict_get_var (r->dict, i);
533
534       if ( 0 == xmlStrcmp (var_spec[i].firstval.type, _xml("date")))
535         {
536           fmt.type = FMT_DATE;
537           fmt.d = 0;
538           fmt.w = 20;
539         }
540       else
541         fmt = fmt_default_for_width (width);
542
543       var_set_both_formats (var, &fmt);
544     }
545
546   /* Create the first case, and cache it */
547   r->used_first_case = false;
548
549   if ( n_var_specs ==  0 )
550     {
551       msg (MW, _("Selected sheet or range of spreadsheet `%s' is empty."),
552            gri->file_name);
553       goto error;
554     }
555
556   r->proto = caseproto_ref (dict_get_proto (r->dict));
557   r->first_case = case_create (r->proto);
558   case_set_missing (r->first_case);
559
560   for ( i = 0 ; i < n_var_specs ; ++i )
561     {
562       const struct variable *var = dict_get_var (r->dict, i);
563
564       convert_xml_to_value (r->first_case, var,  &var_spec[i].firstval);
565     }
566
567   zip_reader_destroy (zreader);
568
569   for ( i = 0 ; i < n_var_specs ; ++i )
570     {
571       free (var_spec[i].firstval.type);
572       free (var_spec[i].firstval.value);
573       free (var_spec[i].firstval.text);
574       free (var_spec[i].name);
575     }
576
577   free (var_spec);
578
579   return casereader_create_sequential
580     (NULL,
581      r->proto,
582      n_cases,
583      &ods_file_casereader_class, r);
584
585  error:
586   
587   zip_reader_destroy (zreader);
588
589   for ( i = 0 ; i < n_var_specs ; ++i )
590     {
591       free (var_spec[i].firstval.type);
592       free (var_spec[i].firstval.value);
593       free (var_spec[i].firstval.text);
594       free (var_spec[i].name);
595     }
596
597   free (var_spec);
598
599   dict_destroy (r->dict);
600   ods_file_casereader_destroy (NULL, r);
601
602
603   return NULL;
604 }
605
606
607 /* Reads and returns one case from READER's file.  Returns a null
608    pointer on failure. */
609 static struct ccase *
610 ods_file_casereader_read (struct casereader *reader UNUSED, void *r_)
611 {
612   struct ccase *c = NULL;
613   xmlChar *val_string = NULL;
614   struct ods_reader *r = r_;
615   int current_row = r->row;
616
617   if ( r->row == -1)
618     return NULL;
619
620   if ( !r->used_first_case )
621     {
622       r->used_first_case = true;
623       return r->first_case;
624     }
625
626
627   if ( r->state > STATE_INIT)
628     {
629       c = case_create (r->proto);
630       case_set_missing (c);
631     }
632
633   while (1 == xmlTextReaderRead (r->xtr))
634     {
635       process_node (r);
636       if ( r->row > current_row)
637         {
638           break;
639         }
640       if ( r->col < r->start_col || (r->stop_col != -1 && r->col > r->stop_col))
641         {
642           continue;
643         }
644       if ( r->col - r->start_col >= caseproto_get_n_widths (r->proto))
645         {
646           continue;
647         }
648       if ( r->stop_row != -1 && r->row > r->stop_row)
649         {
650           continue;
651         }
652       if ( r->state == STATE_CELL &&
653            r->node_type == XML_READER_TYPE_ELEMENT )
654         {
655           val_string = xmlTextReaderGetAttribute (r->xtr, _xml ("office:value"));
656         }
657
658       if ( r->state == STATE_CELL_CONTENT && r->node_type == XML_READER_TYPE_TEXT )
659         {
660           int col;
661           struct xml_value *xmv = xzalloc (sizeof *xmv);
662           xmv->text = xmlTextReaderValue (r->xtr);
663           xmv->value = val_string;
664           val_string = NULL;
665
666           for (col = 0; col < r->span ; ++col)
667             {
668               const int idx = r->col + col - r->start_col;
669
670               const struct variable *var = dict_get_var (r->dict, idx);
671
672               convert_xml_to_value (c, var, xmv);
673             }
674           free (xmv->text);
675           free (xmv->value);
676           free (xmv);
677         }
678
679       if ( r->state < STATE_TABLE)
680         break;
681     }
682
683   if (NULL == c || (r->stop_row != -1 && r->row > r->stop_row + 1))
684     {
685       case_unref (c);
686       return NULL;
687     }
688   else
689     {
690       return c;
691     }
692 }
693 #endif