i18n: Eliminate some translatable strings.
[pspp] / src / data / gnumeric-reader.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2007, 2009 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
18
19 #include <config.h>
20
21 #include <libpspp/message.h>
22 #include <libpspp/misc.h>
23
24 #include "minmax.h"
25
26 #include "gettext.h"
27 #define _(msgid) gettext (msgid)
28 #define N_(msgid) (msgid)
29
30
31 #if !GNM_SUPPORT
32
33 struct casereader *
34 gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
35 {
36   msg (ME, _("Support for Gnumeric files was not compiled into this installation of PSPP"));
37
38   return NULL;
39 }
40
41 #else
42
43 #include <data/casereader-provider.h>
44 #include <errno.h>
45 #include <libpspp/str.h>
46 #include <libpspp/i18n.h>
47 #include <data/dictionary.h>
48 #include <data/variable.h>
49 #include <xalloc.h>
50
51 #include <errno.h>
52 #include <libxml/xmlreader.h>
53 #include <zlib.h>
54 #include <stdbool.h>
55
56 #include <data/case.h>
57 #include <data/value.h>
58
59 #include "gnumeric-reader.h"
60 #include <data/identifier.h>
61 #include <assert.h>
62
63 /* Default width of string variables. */
64 #define GNUMERIC_DEFAULT_WIDTH 8
65
66 static void gnm_file_casereader_destroy (struct casereader *, void *);
67
68 static struct ccase *gnm_file_casereader_read (struct casereader *, void *);
69
70 static const struct casereader_class gnm_file_casereader_class =
71   {
72     gnm_file_casereader_read,
73     gnm_file_casereader_destroy,
74     NULL,
75     NULL,
76   };
77
78 /* Convert a string, which is an integer encoded in base26
79    IE, A=0, B=1, ... Z=25 to the integer it represents.
80    ... except that in this scheme, digits with an exponent
81    greater than 1 are implicitly incremented by 1, so
82    AA  = 0 + 1*26, AB = 1 + 1*26,
83    ABC = 2 + 2*26 + 1*26^2 ....
84 */
85 static int
86 pseudo_base26 (const char *str)
87 {
88   int i;
89   int multiplier = 1;
90   int result = 0;
91   int len = strlen (str);
92
93   for ( i = len - 1 ; i >= 0; --i)
94     {
95       int mantissa = (str[i] - 'A');
96
97       if ( mantissa < 0 || mantissa > 25 )
98         return -1;
99
100       if ( i != len - 1)
101         mantissa++;
102
103       result += mantissa * multiplier;
104
105       multiplier *= 26;
106     }
107
108   return result;
109 }
110
111
112
113 /* Convert a cell reference in the form "A1:B2", to
114    integers.  A1 means column zero, row zero.
115    B1 means column 1 row 0. AA1 means column 26, row 0.
116 */
117 static bool
118 convert_cell_ref (const char *ref,
119                   int *col0, int *row0,
120                   int *coli, int *rowi)
121 {
122   char startcol[5];
123   char stopcol [5];
124
125   int startrow;
126   int stoprow;
127
128   int n = sscanf (ref, "%4[a-zA-Z]%d:%4[a-zA-Z]%d",
129               startcol, &startrow,
130               stopcol, &stoprow);
131   if ( n != 4)
132     return false;
133
134   str_uppercase (startcol);
135   *col0 = pseudo_base26 (startcol);
136   str_uppercase (stopcol);
137   *coli = pseudo_base26 (stopcol);
138   *row0 = startrow - 1;
139   *rowi = stoprow - 1 ;
140
141   return true;
142 }
143
144
145 enum reader_state
146   {
147     STATE_INIT = 0,        /* Initial state */
148     STATE_SHEET_START,     /* Found the start of a sheet */
149     STATE_SHEET_NAME,      /* Found the sheet name */
150     STATE_MAXROW,
151     STATE_SHEET_FOUND,     /* Found the sheet that we actually want */
152     STATE_CELLS_START,     /* Found the start of the cell array */
153     STATE_CELL             /* Found a cell */
154   };
155
156
157 struct gnumeric_reader
158 {
159   xmlTextReaderPtr xtr;
160
161   enum reader_state state;
162   int row;
163   int col;
164   int node_type;
165   int sheet_index;
166
167
168   const xmlChar *target_sheet;
169   int target_sheet_index;
170
171   int start_row;
172   int start_col;
173   int stop_row;
174   int stop_col;
175
176   struct caseproto *proto;
177   struct dictionary *dict;
178   struct ccase *first_case;
179   bool used_first_case;
180 };
181
182 static void process_node (struct gnumeric_reader *r);
183
184 #define _xml(X) (const xmlChar *)(X)
185
186 #define _xmlchar_to_int(X) atoi((const char *)X)
187
188 static void
189 gnm_file_casereader_destroy (struct casereader *reader UNUSED, void *r_)
190 {
191   struct gnumeric_reader *r = r_;
192   if ( r == NULL)
193         return ;
194
195   if ( r->xtr)
196     xmlFreeTextReader (r->xtr);
197
198   if ( ! r->used_first_case )
199     case_unref (r->first_case);
200
201   caseproto_unref (r->proto);
202
203   free (r);
204 }
205
206 static void
207 process_node (struct gnumeric_reader *r)
208 {
209   xmlChar *name = xmlTextReaderName (r->xtr);
210   if (name == NULL)
211     name = xmlStrdup (_xml ("--"));
212
213
214   r->node_type = xmlTextReaderNodeType (r->xtr);
215
216   switch ( r->state)
217     {
218     case STATE_INIT:
219       if (0 == xmlStrcasecmp (name, _xml("gnm:Sheet")) &&
220           XML_READER_TYPE_ELEMENT  == r->node_type)
221         {
222           r->state = STATE_SHEET_START;
223         }
224       break;
225     case STATE_SHEET_START:
226       if (0 == xmlStrcasecmp (name, _xml("gnm:Name"))  &&
227           XML_READER_TYPE_ELEMENT  == r->node_type)
228         {
229           r->state = STATE_SHEET_NAME;
230         }
231       else if (0 == xmlStrcasecmp (name, _xml("gnm:Name"))  &&
232                XML_READER_TYPE_END_ELEMENT  == r->node_type)
233         {
234           r->state = STATE_INIT;
235         }
236       break;
237     case STATE_SHEET_NAME:
238       if (0 == xmlStrcasecmp (name, _xml("gnm:Name"))  &&
239           XML_READER_TYPE_END_ELEMENT  == r->node_type)
240         {
241           r->state = STATE_SHEET_START;
242         }
243       else if (XML_READER_TYPE_TEXT == r->node_type)
244         {
245           ++r->sheet_index;
246           if ( r->target_sheet != NULL)
247             {
248               xmlChar *value = xmlTextReaderValue (r->xtr);
249               if ( 0 == xmlStrcmp (value, r->target_sheet))
250                 r->state = STATE_SHEET_FOUND;
251               free (value);
252             }
253           else if (r->target_sheet_index == r->sheet_index)
254             {
255               r->state = STATE_SHEET_FOUND;
256             }
257         }
258       break;
259     case STATE_SHEET_FOUND:
260       if (0 == xmlStrcasecmp (name, _xml("gnm:Cells"))  &&
261           XML_READER_TYPE_ELEMENT  == r->node_type)
262         {
263           if (! xmlTextReaderIsEmptyElement (r->xtr))
264             r->state = STATE_CELLS_START;
265         }
266       else if (0 == xmlStrcasecmp (name, _xml("gnm:MaxRow"))  &&
267           XML_READER_TYPE_ELEMENT  == r->node_type)
268         {
269           r->state = STATE_MAXROW;
270         }
271       else if (0 == xmlStrcasecmp (name, _xml("gnm:Sheet"))  &&
272           XML_READER_TYPE_END_ELEMENT  == r->node_type)
273         {
274           r->state = STATE_INIT;
275         }
276       break;
277     case STATE_MAXROW:
278       if (0 == xmlStrcasecmp (name, _xml("gnm:MaxRow"))  &&
279           XML_READER_TYPE_END_ELEMENT  == r->node_type)
280         {
281           r->state = STATE_SHEET_FOUND;
282         }
283     case STATE_CELLS_START:
284       if (0 == xmlStrcasecmp (name, _xml ("gnm:Cell"))  &&
285           XML_READER_TYPE_ELEMENT  == r->node_type)
286         {
287           xmlChar *attr = NULL;
288           r->state = STATE_CELL;
289
290           attr = xmlTextReaderGetAttribute (r->xtr, _xml ("Col"));
291           r->col =  _xmlchar_to_int (attr);
292           free (attr);
293
294           attr = xmlTextReaderGetAttribute (r->xtr, _xml ("Row"));
295           r->row = _xmlchar_to_int (attr);
296           free (attr);
297         }
298       else if (0 == xmlStrcasecmp (name, _xml("gnm:Cells"))  &&
299                XML_READER_TYPE_END_ELEMENT  == r->node_type)
300         r->state = STATE_SHEET_NAME;
301
302       break;
303     case STATE_CELL:
304       if (0 == xmlStrcasecmp (name, _xml("gnm:Cell"))  &&
305                               XML_READER_TYPE_END_ELEMENT  == r->node_type)
306         r->state = STATE_CELLS_START;
307       break;
308     default:
309       break;
310     };
311
312   xmlFree (name);
313 }
314
315
316 /*
317    Sets the VAR of case C, to the value corresponding to the xml string XV
318  */
319 static void
320 convert_xml_string_to_value (struct ccase *c, const struct variable *var,
321                              const xmlChar *xv)
322 {
323   int n_bytes = 0;
324   union value *v = case_data_rw (c, var);
325
326   const char *text = (const char *) xv;
327
328   if ( text)
329     n_bytes = MIN (var_get_width (var), strlen (text));
330
331   if ( var_is_alpha (var))
332     {
333       memcpy (value_str_rw (v, var_get_width (var)), text, n_bytes);
334     }
335   else
336     {
337       char *endptr;
338       errno = 0;
339       v->f = strtod (text, &endptr);
340       if ( errno != 0 || endptr == text)
341         v->f = SYSMIS;
342     }
343 }
344
345 struct var_spec
346 {
347   char *name;
348   int width;
349   xmlChar *first_value;
350 };
351
352 struct casereader *
353 gnumeric_open_reader (struct gnumeric_read_info *gri, struct dictionary **dict)
354 {
355   unsigned long int vstart = 0;
356   int ret;
357   casenumber n_cases = CASENUMBER_MAX;
358   int i;
359   struct var_spec *var_spec = NULL;
360   int n_var_specs = 0;
361
362   struct gnumeric_reader *r = NULL;
363
364   gzFile gz = gzopen (gri->file_name, "r");
365
366   if ( NULL == gz)
367     {
368       msg (ME, _("Error opening \"%s\" for reading as a Gnumeric file: %s."),
369            gri->file_name, strerror (errno));
370
371       goto error;
372     }
373
374   r = xzalloc (sizeof *r);
375
376   r->xtr = xmlReaderForIO ((xmlInputReadCallback) gzread, gzclose, gz,
377                            NULL, NULL, 0);
378
379   if ( r->xtr == NULL)
380     goto error;
381
382   if ( gri->cell_range )
383     {
384       if ( ! convert_cell_ref (gri->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                gri->cell_range);
390           goto error;
391         }
392     }
393   else
394     {
395       r->start_col = 0;
396       r->start_row = 0;
397       r->stop_col = -1;
398       r->stop_row = -1;
399     }
400
401   r->state = STATE_INIT;
402   r->target_sheet = BAD_CAST gri->sheet_name;
403   r->target_sheet_index = gri->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 ( gri->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           n_var_specs =  idx + 1 ;
457           var_spec = xrealloc (var_spec, sizeof (*var_spec) * n_var_specs);
458           var_spec [idx].name = NULL;
459           var_spec [idx].width = -1;
460           var_spec [idx].first_value = NULL;
461         }
462
463       if ( r->node_type == XML_READER_TYPE_TEXT )
464         {
465           xmlChar *value = xmlTextReaderValue (r->xtr);
466           const char *text  = (const char *) value;
467
468           if ( r->row < r->start_row)
469             {
470               if ( gri->read_names )
471                 {
472                   var_spec [idx].name = xstrdup (text);
473                 }
474             }
475           else
476             {
477               var_spec [idx].first_value = xmlStrdup (value);
478
479               if (-1 ==  var_spec [idx].width )
480                 var_spec [idx].width = (gri->asw == -1) ?
481                   ROUND_UP (strlen(text), GNUMERIC_DEFAULT_WIDTH) : gri->asw;
482             }
483
484           free (value);
485         }
486       else if ( r->node_type == XML_READER_TYPE_ELEMENT
487                 && r->state == STATE_CELL)
488         {
489           if ( r->row == r->start_row )
490             {
491               xmlChar *attr =
492                 xmlTextReaderGetAttribute (r->xtr, _xml ("ValueType"));
493
494               if ( NULL == attr || 60 !=  _xmlchar_to_int (attr))
495                 var_spec [idx].width = 0;
496
497               free (attr);
498             }
499         }
500     }
501
502
503   /* Create the dictionary and populate it */
504   *dict = r->dict = dict_create ();
505
506   dict_set_encoding (r->dict, (const char *) xmlTextReaderConstEncoding (r->xtr));
507
508   for (i = 0 ; i < n_var_specs ; ++i )
509     {
510       char name[VAR_NAME_LEN + 1];
511
512       /* Probably no data exists for this variable, so allocate a
513          default width */
514       if ( var_spec[i].width == -1 )
515         var_spec[i].width = GNUMERIC_DEFAULT_WIDTH;
516
517       if  ( ! dict_make_unique_var_name (r->dict, var_spec[i].name,
518                                          &vstart, name))
519         {
520           msg (ME, _("Cannot create variable name from %s"), var_spec[i].name);
521           goto error;
522         }
523
524       dict_create_var (r->dict, name, var_spec[i].width);
525     }
526
527   /* Create the first case, and cache it */
528   r->used_first_case = false;
529
530   if ( n_var_specs ==  0 )
531     {
532       msg (MW, _("Selected sheet or range of spreadsheet \"%s\" is empty."),
533            gri->file_name);
534       goto error;
535     }
536
537   r->proto = caseproto_ref (dict_get_proto (r->dict));
538   r->first_case = case_create (r->proto);
539   case_set_missing (r->first_case);
540
541   for ( i = 0 ; i < n_var_specs ; ++i )
542     {
543       const struct variable *var = dict_get_var (r->dict, i);
544
545       convert_xml_string_to_value (r->first_case, var,
546                                    var_spec[i].first_value);
547     }
548
549   for ( i = 0 ; i < n_var_specs ; ++i )
550     {
551       free (var_spec[i].first_value);
552       free (var_spec[i].name);
553     }
554
555   free (var_spec);
556
557   return casereader_create_sequential
558     (NULL,
559      r->proto,
560      n_cases,
561      &gnm_file_casereader_class, r);
562
563
564  error:
565   for ( i = 0 ; i < n_var_specs ; ++i )
566     {
567       free (var_spec[i].first_value);
568       free (var_spec[i].name);
569     }
570
571   free (var_spec);
572   dict_destroy (*dict);
573
574   gnm_file_casereader_destroy (NULL, r);
575
576   return NULL;
577 };
578
579
580 /* Reads and returns one case from READER's file.  Returns a null
581    pointer on failure. */
582 static struct ccase *
583 gnm_file_casereader_read (struct casereader *reader UNUSED, void *r_)
584 {
585   struct ccase *c;
586   int ret = 0;
587
588   struct gnumeric_reader *r = r_;
589   int current_row = r->row;
590
591   if ( !r->used_first_case )
592     {
593       r->used_first_case = true;
594       return r->first_case;
595     }
596
597   c = case_create (r->proto);
598   case_set_missing (c);
599
600   while ((r->state == STATE_CELL || r->state == STATE_CELLS_START )
601          && r->row == current_row && (ret = xmlTextReaderRead (r->xtr)))
602     {
603       process_node (r);
604
605       if ( r->col < r->start_col || (r->stop_col != -1 &&
606                                      r->col > r->stop_col))
607         continue;
608
609       if ( r->col - r->start_col >= caseproto_get_n_widths (r->proto))
610         continue;
611
612       if ( r->stop_row != -1 && r->row > r->stop_row)
613         break;
614
615       if ( r->node_type == XML_READER_TYPE_TEXT )
616         {
617           xmlChar *value = xmlTextReaderValue (r->xtr);
618
619           const int idx = r->col - r->start_col;
620
621           const struct variable *var = dict_get_var (r->dict, idx);
622
623           convert_xml_string_to_value (c, var, value);
624
625           free (value);
626         }
627
628     }
629
630   if (ret == 1)
631     return c;
632   else
633     {
634       case_unref (c);
635       return NULL;
636     }
637 }
638
639
640 #endif /* GNM_SUPPORT */