3fccde9c9833e5542974b5d1c8dec609167e8120
[pspp] / src / output / table.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2011, 2014, 2016 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 "output/table.h"
20 #include "output/table-provider.h"
21
22 #include <assert.h>
23 #include <stdlib.h>
24
25 #include "libpspp/cast.h"
26 #include "libpspp/compiler.h"
27 #include "libpspp/str.h"
28 #include "output/table-item.h"
29
30 #include "gl/xalloc.h"
31
32 /* Increases TABLE's reference count, indicating that it has an additional
33    owner.  An table that is shared among multiple owners must not be
34    modified. */
35 struct table *
36 table_ref (const struct table *table_)
37 {
38   struct table *table = CONST_CAST (struct table *, table_);
39   table->ref_cnt++;
40   return table;
41 }
42
43 /* Decreases TABLE's reference count, indicating that it has one fewer owner.
44    If TABLE no longer has any owners, it is freed. */
45 void
46 table_unref (struct table *table)
47 {
48   if (table != NULL)
49     {
50       assert (table->ref_cnt > 0);
51       if (--table->ref_cnt == 0)
52         table->klass->destroy (table);
53     }
54 }
55
56 /* Returns true if TABLE has more than one owner.  A table item that is shared
57    among multiple owners must not be modified. */
58 bool
59 table_is_shared (const struct table *table)
60 {
61   return table->ref_cnt > 1;
62 }
63
64 /* Sets the number of left header columns in TABLE to HL. */
65 void
66 table_set_hl (struct table *table, int hl)
67 {
68   assert (!table_is_shared (table));
69   table->h[TABLE_HORZ][0] = hl;
70 }
71
72 /* Sets the number of right header columns in TABLE to HR. */
73 void
74 table_set_hr (struct table *table, int hr)
75 {
76   assert (!table_is_shared (table));
77   table->h[TABLE_HORZ][1] = hr;
78 }
79
80 /* Sets the number of top header rows in TABLE to HT. */
81 void
82 table_set_ht (struct table *table, int ht)
83 {
84   assert (!table_is_shared (table));
85   table->h[TABLE_VERT][0] = ht;
86 }
87
88 /* Sets the number of top header rows in TABLE to HB. */
89 void
90 table_set_hb (struct table *table, int hb)
91 {
92   assert (!table_is_shared (table));
93   table->h[TABLE_VERT][1] = hb;
94 }
95 \f
96 /* Initializes TABLE as a table of the specified CLASS, initially with a
97    reference count of 1.
98
99    TABLE initially has 0 rows and columns and no headers.  The table
100    implementation should update the numbers of rows and columns.  The table
101    implementation (or its client) may update the header rows and columns.
102
103    A table is an abstract class, that is, a plain struct table is not useful on
104    its own.  Thus, this function is normally called from the initialization
105    function of some subclass of table. */
106 void
107 table_init (struct table *table, const struct table_class *class)
108 {
109   table->klass = class;
110   table->n[TABLE_HORZ] = table->n[TABLE_VERT] = 0;
111   table->h[TABLE_HORZ][0] = table->h[TABLE_HORZ][1] = 0;
112   table->h[TABLE_VERT][0] = table->h[TABLE_VERT][1] = 0;
113   table->ref_cnt = 1;
114 }
115
116 /* Sets the number of columns in TABLE to NC. */
117 void
118 table_set_nc (struct table *table, int nc)
119 {
120   assert (!table_is_shared (table));
121   table->n[TABLE_HORZ] = nc;
122 }
123
124 /* Sets the number of rows in TABLE to NR. */
125 void
126 table_set_nr (struct table *table, int nr)
127 {
128   assert (!table_is_shared (table));
129   table->n[TABLE_VERT] = nr;
130 }
131 \f
132 struct cell_style *
133 cell_style_clone (const struct cell_style *old)
134 {
135   struct cell_style *new = xmalloc (sizeof *new);
136   *new = *old;
137   if (new->font)
138     new->font = strdup (new->font);
139   return new;
140 }
141
142 void
143 cell_style_free (struct cell_style *style)
144 {
145   if (style)
146     {
147       free (style->font);
148       free (style);
149     }
150 }
151
152 /* Initializes CELL with the contents of the table cell at column X and row Y
153    within TABLE.  When CELL is no longer needed, the caller is responsible for
154    freeing it by calling table_cell_free(CELL).
155
156    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
157 void
158 table_get_cell (const struct table *table, int x, int y,
159                 struct table_cell *cell)
160 {
161   assert (x >= 0 && x < table->n[TABLE_HORZ]);
162   assert (y >= 0 && y < table->n[TABLE_VERT]);
163
164   static const struct cell_style default_style = CELL_STYLE_INITIALIZER;
165   cell->style = &default_style;
166
167   table->klass->get_cell (table, x, y, cell);
168 }
169
170 /* Frees CELL, which should have been initialized by calling
171    table_get_cell(). */
172 void
173 table_cell_free (struct table_cell *cell)
174 {
175   if (cell->destructor != NULL)
176     cell->destructor (cell->destructor_aux);
177 }
178
179 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
180    representing a rule running alongside one of the cells in TABLE.
181
182    Suppose NC is the number of columns in TABLE and NR is the number of rows.
183    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
184    (0,0), the return value is the rule that runs vertically on the left side of
185    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
186    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
187    cell (NC-1,0).
188
189    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
190    within a 7x7 table.  The '|' characters at the intersection of the X labels
191    and Y labels show the rule whose style would be returned by calling
192    table_get_rule with those X and Y values:
193
194                            0  1  2  3  4  5  6  7
195                            +--+--+--+--+--+--+--+
196                          0 |  |  |  |  |  |  |  |
197                            +--+--+--+--+--+--+--+
198                          1 |  |  |  |  |  |  |  |
199                            +--+--+--+--+--+--+--+
200                          2 |  |  |  |  |  |  |  |
201                            +--+--+--+--+--+--+--+
202                          3 |  |  |  |  |  |  |  |
203                            +--+--+--+--+--+--+--+
204                          4 |  |  |  |  |  |  |  |
205                            +--+--+--+--+--+--+--+
206                          5 |  |  |  |  |  |  |  |
207                            +--+--+--+--+--+--+--+
208                          6 |  |  |  |  |  |  |  |
209                            +--+--+--+--+--+--+--+
210
211    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
212    (X,Y) = (0,0), the return value is the rule that runs horizontally above
213    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
214    between that cell and cell (0,1); and so on, up to (0,NR), which runs
215    horizontally below cell (0,NR-1). */
216 int
217 table_get_rule (const struct table *table, enum table_axis axis, int x, int y)
218 {
219   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
220   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
221   return table->klass->get_rule (table, axis, x, y);
222 }
223
224 void
225 cell_contents_format_footnote_markers (const struct cell_contents *c,
226                                        struct string *s)
227 {
228   for (size_t i = 0; i < c->n_footnotes; i++)
229     {
230       if (i)
231         ds_put_byte (s, ',');
232       ds_put_cstr (s, c->footnotes[i]->marker);
233     }
234 }
235
236 static const struct footnote **
237 add_footnotes (const struct footnote **refs, size_t n_refs,
238                const struct footnote **footnotes, size_t *allocated, size_t *n)
239 {
240   for (size_t i = 0; i < n_refs; i++)
241     {
242       const struct footnote *f = refs[i];
243       if (f->idx >= *allocated)
244         {
245           size_t new_allocated = (f->idx + 1) * 2;
246           footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
247           while (*allocated < new_allocated)
248             footnotes[(*allocated)++] = NULL;
249         }
250       footnotes[f->idx] = f;
251       if (f->idx >= *n)
252         *n = f->idx + 1;
253     }
254   return footnotes;
255 }
256
257 size_t
258 table_collect_footnotes (const struct table_item *item,
259                          const struct footnote ***footnotesp)
260 {
261   const struct footnote **footnotes = NULL;
262   size_t allocated = 0;
263   size_t n = 0;
264
265   struct table *t = item->table;
266   for (int y = 0; y < table_nr (t); y++)
267     {
268       struct table_cell cell;
269       for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
270         {
271           table_get_cell (t, x, y, &cell);
272
273           if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
274             for (size_t i = 0; i < cell.n_contents; i++)
275               {
276                 const struct cell_contents *c = &cell.contents[i];
277                 footnotes = add_footnotes (c->footnotes, c->n_footnotes,
278                                            footnotes, &allocated, &n);
279               }
280           table_cell_free (&cell);
281         }
282     }
283
284   const struct table_item_text *title = table_item_get_title (item);
285   if (title)
286     footnotes = add_footnotes (title->footnotes, title->n_footnotes,
287                                footnotes, &allocated, &n);
288
289   const struct table_item_text *caption = table_item_get_caption (item);
290   if (caption)
291     footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
292                                footnotes, &allocated, &n);
293
294   *footnotesp = footnotes;
295   return n;
296 }
297 \f
298 struct table_unshared
299   {
300     struct table table;
301     struct table *subtable;
302   };
303
304 static const struct table_class table_unshared_class;
305
306 /* Takes ownership of TABLE and returns a table with the same contents but
307    which is guaranteed not to be shared (as returned by table_is_shared()).
308
309    If TABLE is unshared, just returns TABLE.
310
311    The only real use for this function is to create a copy of TABLE in which
312    the headers can be adjusted, which is a pretty specialized use case. */
313 struct table *
314 table_unshare (struct table *table)
315 {
316   if (!table_is_shared (table))
317     return table;
318   else
319     {
320       struct table_unshared *tiu = xmalloc (sizeof *tiu);
321       table_init (&tiu->table, &table_unshared_class);
322       table_set_nc (&tiu->table, table_nc (table));
323       table_set_nr (&tiu->table, table_nr (table));
324       table_set_hl (&tiu->table, table_hl (table));
325       table_set_hr (&tiu->table, table_hr (table));
326       table_set_ht (&tiu->table, table_ht (table));
327       table_set_hb (&tiu->table, table_hb (table));
328       tiu->subtable = table;
329       return &tiu->table;
330     }
331 }
332
333 static struct table_unshared *
334 table_unshared_cast (const struct table *table)
335 {
336   assert (table->klass == &table_unshared_class);
337   return UP_CAST (table, struct table_unshared, table);
338 }
339
340 static void
341 table_unshared_destroy (struct table *tiu_)
342 {
343   struct table_unshared *tiu = table_unshared_cast (tiu_);
344   table_unref (tiu->subtable);
345   free (tiu);
346 }
347
348 static void
349 table_unshared_get_cell (const struct table *tiu_, int x, int y,
350                               struct table_cell *cell)
351 {
352   struct table_unshared *tiu = table_unshared_cast (tiu_);
353   table_get_cell (tiu->subtable, x, y, cell);
354 }
355
356 static int
357 table_unshared_get_rule (const struct table *tiu_,
358                               enum table_axis axis, int x, int y)
359 {
360   struct table_unshared *tiu = table_unshared_cast (tiu_);
361   return table_get_rule (tiu->subtable, axis, x, y);
362 }
363
364 static const struct table_class table_unshared_class =
365   {
366     table_unshared_destroy,
367     table_unshared_get_cell,
368     table_unshared_get_rule,
369     NULL,                       /* paste */
370     NULL,                       /* select */
371   };
372 \f
373 struct table_string
374   {
375     struct table table;
376     char *string;
377     unsigned int options;
378   };
379
380 static const struct table_class table_string_class;
381
382 /* Returns a table that contains a single cell, whose contents are S with
383    options OPTIONS (a combination of TAB_* values).  */
384 struct table *
385 table_from_string (unsigned int options, const char *s)
386 {
387   struct table_string *ts = xmalloc (sizeof *ts);
388   table_init (&ts->table, &table_string_class);
389   ts->table.n[TABLE_HORZ] = ts->table.n[TABLE_VERT] = 1;
390   ts->string = xstrdup (s);
391   ts->options = options;
392   return &ts->table;
393 }
394
395 static struct table_string *
396 table_string_cast (const struct table *table)
397 {
398   assert (table->klass == &table_string_class);
399   return UP_CAST (table, struct table_string, table);
400 }
401
402 static void
403 table_string_destroy (struct table *ts_)
404 {
405   struct table_string *ts = table_string_cast (ts_);
406   free (ts->string);
407   free (ts);
408 }
409
410 static void
411 table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED,
412                        struct table_cell *cell)
413 {
414   struct table_string *ts = table_string_cast (ts_);
415   cell->d[TABLE_HORZ][0] = 0;
416   cell->d[TABLE_HORZ][1] = 1;
417   cell->d[TABLE_VERT][0] = 0;
418   cell->d[TABLE_VERT][1] = 1;
419   cell->contents = &cell->inline_contents;
420   cell->inline_contents.options = ts->options;
421   cell->inline_contents.text = ts->string;
422   cell->inline_contents.n_footnotes = 0;
423   cell->n_contents = 1;
424   cell->destructor = NULL;
425 }
426
427
428 static int
429 table_string_get_rule (const struct table *ts UNUSED,
430                        enum table_axis axis UNUSED, int x UNUSED, int y UNUSED)
431 {
432   return TAL_0;
433 }
434
435 static const struct table_class table_string_class =
436   {
437     table_string_destroy,
438     table_string_get_cell,
439     table_string_get_rule,
440     NULL,                       /* paste */
441     NULL,                       /* select */
442   };