0cd05d7039837f735085772a8dca2854600fe2b2
[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 /* Initializes CELL with the contents of the table cell at column X and row Y
133    within TABLE.  When CELL is no longer needed, the caller is responsible for
134    freeing it by calling table_cell_free(CELL).
135
136    The caller must ensure that CELL is destroyed before TABLE is unref'ed. */
137 void
138 table_get_cell (const struct table *table, int x, int y,
139                 struct table_cell *cell)
140 {
141   assert (x >= 0 && x < table->n[TABLE_HORZ]);
142   assert (y >= 0 && y < table->n[TABLE_VERT]);
143
144   static const struct cell_style default_style =
145     {
146       .fg = { 0, 0, 0 },
147       .bg = { 255, 255, 255 },
148       .margin = { [TABLE_HORZ][0] = 8, [TABLE_HORZ][1] = 11,
149                   [TABLE_VERT][0] = 1, [TABLE_VERT][1] = 1 },
150     };
151   cell->style = &default_style;
152
153   table->klass->get_cell (table, x, y, cell);
154 }
155
156 /* Frees CELL, which should have been initialized by calling
157    table_get_cell(). */
158 void
159 table_cell_free (struct table_cell *cell)
160 {
161   if (cell->destructor != NULL)
162     cell->destructor (cell->destructor_aux);
163 }
164
165 /* Returns one of the TAL_* enumeration constants (declared in output/table.h)
166    representing a rule running alongside one of the cells in TABLE.
167
168    Suppose NC is the number of columns in TABLE and NR is the number of rows.
169    Then, if AXIS is TABLE_HORZ, then 0 <= X <= NC and 0 <= Y < NR.  If (X,Y) =
170    (0,0), the return value is the rule that runs vertically on the left side of
171    cell (0,0); if (X,Y) = (1,0), it is the vertical rule between that cell and
172    cell (1,0); and so on, up to (NC,0), which runs vertically on the right of
173    cell (NC-1,0).
174
175    The following diagram illustrates the meaning of (X,Y) for AXIS = TABLE_HORZ
176    within a 7x7 table.  The '|' characters at the intersection of the X labels
177    and Y labels show the rule whose style would be returned by calling
178    table_get_rule with those X and Y values:
179
180                            0  1  2  3  4  5  6  7
181                            +--+--+--+--+--+--+--+
182                          0 |  |  |  |  |  |  |  |
183                            +--+--+--+--+--+--+--+
184                          1 |  |  |  |  |  |  |  |
185                            +--+--+--+--+--+--+--+
186                          2 |  |  |  |  |  |  |  |
187                            +--+--+--+--+--+--+--+
188                          3 |  |  |  |  |  |  |  |
189                            +--+--+--+--+--+--+--+
190                          4 |  |  |  |  |  |  |  |
191                            +--+--+--+--+--+--+--+
192                          5 |  |  |  |  |  |  |  |
193                            +--+--+--+--+--+--+--+
194                          6 |  |  |  |  |  |  |  |
195                            +--+--+--+--+--+--+--+
196
197    Similarly, if AXIS is TABLE_VERT, then 0 <= X < NC and 0 <= Y <= NR.  If
198    (X,Y) = (0,0), the return value is the rule that runs horizontally above
199    the top of cell (0,0); if (X,Y) = (0,1), it is the horizontal rule
200    between that cell and cell (0,1); and so on, up to (0,NR), which runs
201    horizontally below cell (0,NR-1). */
202 int
203 table_get_rule (const struct table *table, enum table_axis axis, int x, int y)
204 {
205   assert (x >= 0 && x < table->n[TABLE_HORZ] + (axis == TABLE_HORZ));
206   assert (y >= 0 && y < table->n[TABLE_VERT] + (axis == TABLE_VERT));
207   return table->klass->get_rule (table, axis, x, y);
208 }
209
210 void
211 cell_contents_format_footnote_markers (const struct cell_contents *c,
212                                        struct string *s)
213 {
214   for (size_t i = 0; i < c->n_footnotes; i++)
215     {
216       if (i)
217         ds_put_byte (s, ',');
218       ds_put_cstr (s, c->footnotes[i]->marker);
219     }
220 }
221
222 static const struct footnote **
223 add_footnotes (const struct footnote **refs, size_t n_refs,
224                const struct footnote **footnotes, size_t *allocated, size_t *n)
225 {
226   for (size_t i = 0; i < n_refs; i++)
227     {
228       const struct footnote *f = refs[i];
229       if (f->idx >= *allocated)
230         {
231           size_t new_allocated = (f->idx + 1) * 2;
232           footnotes = xrealloc (footnotes, new_allocated * sizeof *footnotes);
233           while (*allocated < new_allocated)
234             footnotes[(*allocated)++] = NULL;
235         }
236       footnotes[f->idx] = f;
237       if (f->idx >= *n)
238         *n = f->idx + 1;
239     }
240   return footnotes;
241 }
242
243 size_t
244 table_collect_footnotes (const struct table_item *item,
245                          const struct footnote ***footnotesp)
246 {
247   const struct footnote **footnotes = NULL;
248   size_t allocated = 0;
249   size_t n = 0;
250
251   struct table *t = item->table;
252   for (int y = 0; y < table_nr (t); y++)
253     {
254       struct table_cell cell;
255       for (int x = 0; x < table_nc (t); x = cell.d[TABLE_HORZ][1])
256         {
257           table_get_cell (t, x, y, &cell);
258
259           if (x == cell.d[TABLE_HORZ][0] && y == cell.d[TABLE_VERT][0])
260             for (size_t i = 0; i < cell.n_contents; i++)
261               {
262                 const struct cell_contents *c = &cell.contents[i];
263                 footnotes = add_footnotes (c->footnotes, c->n_footnotes,
264                                            footnotes, &allocated, &n);
265               }
266           table_cell_free (&cell);
267         }
268     }
269
270   const struct table_item_text *title = table_item_get_title (item);
271   if (title)
272     footnotes = add_footnotes (title->footnotes, title->n_footnotes,
273                                footnotes, &allocated, &n);
274
275   const struct table_item_text *caption = table_item_get_caption (item);
276   if (caption)
277     footnotes = add_footnotes (caption->footnotes, caption->n_footnotes,
278                                footnotes, &allocated, &n);
279
280   *footnotesp = footnotes;
281   return n;
282 }
283 \f
284 struct table_unshared
285   {
286     struct table table;
287     struct table *subtable;
288   };
289
290 static const struct table_class table_unshared_class;
291
292 /* Takes ownership of TABLE and returns a table with the same contents but
293    which is guaranteed not to be shared (as returned by table_is_shared()).
294
295    If TABLE is unshared, just returns TABLE.
296
297    The only real use for this function is to create a copy of TABLE in which
298    the headers can be adjusted, which is a pretty specialized use case. */
299 struct table *
300 table_unshare (struct table *table)
301 {
302   if (!table_is_shared (table))
303     return table;
304   else
305     {
306       struct table_unshared *tiu = xmalloc (sizeof *tiu);
307       table_init (&tiu->table, &table_unshared_class);
308       table_set_nc (&tiu->table, table_nc (table));
309       table_set_nr (&tiu->table, table_nr (table));
310       table_set_hl (&tiu->table, table_hl (table));
311       table_set_hr (&tiu->table, table_hr (table));
312       table_set_ht (&tiu->table, table_ht (table));
313       table_set_hb (&tiu->table, table_hb (table));
314       tiu->subtable = table;
315       return &tiu->table;
316     }
317 }
318
319 static struct table_unshared *
320 table_unshared_cast (const struct table *table)
321 {
322   assert (table->klass == &table_unshared_class);
323   return UP_CAST (table, struct table_unshared, table);
324 }
325
326 static void
327 table_unshared_destroy (struct table *tiu_)
328 {
329   struct table_unshared *tiu = table_unshared_cast (tiu_);
330   table_unref (tiu->subtable);
331   free (tiu);
332 }
333
334 static void
335 table_unshared_get_cell (const struct table *tiu_, int x, int y,
336                               struct table_cell *cell)
337 {
338   struct table_unshared *tiu = table_unshared_cast (tiu_);
339   table_get_cell (tiu->subtable, x, y, cell);
340 }
341
342 static int
343 table_unshared_get_rule (const struct table *tiu_,
344                               enum table_axis axis, int x, int y)
345 {
346   struct table_unshared *tiu = table_unshared_cast (tiu_);
347   return table_get_rule (tiu->subtable, axis, x, y);
348 }
349
350 static const struct table_class table_unshared_class =
351   {
352     table_unshared_destroy,
353     table_unshared_get_cell,
354     table_unshared_get_rule,
355     NULL,                       /* paste */
356     NULL,                       /* select */
357   };
358 \f
359 struct table_string
360   {
361     struct table table;
362     char *string;
363     unsigned int options;
364   };
365
366 static const struct table_class table_string_class;
367
368 /* Returns a table that contains a single cell, whose contents are S with
369    options OPTIONS (a combination of TAB_* values).  */
370 struct table *
371 table_from_string (unsigned int options, const char *s)
372 {
373   struct table_string *ts = xmalloc (sizeof *ts);
374   table_init (&ts->table, &table_string_class);
375   ts->table.n[TABLE_HORZ] = ts->table.n[TABLE_VERT] = 1;
376   ts->string = xstrdup (s);
377   ts->options = options;
378   return &ts->table;
379 }
380
381 static struct table_string *
382 table_string_cast (const struct table *table)
383 {
384   assert (table->klass == &table_string_class);
385   return UP_CAST (table, struct table_string, table);
386 }
387
388 static void
389 table_string_destroy (struct table *ts_)
390 {
391   struct table_string *ts = table_string_cast (ts_);
392   free (ts->string);
393   free (ts);
394 }
395
396 static void
397 table_string_get_cell (const struct table *ts_, int x UNUSED, int y UNUSED,
398                        struct table_cell *cell)
399 {
400   struct table_string *ts = table_string_cast (ts_);
401   cell->d[TABLE_HORZ][0] = 0;
402   cell->d[TABLE_HORZ][1] = 1;
403   cell->d[TABLE_VERT][0] = 0;
404   cell->d[TABLE_VERT][1] = 1;
405   cell->contents = &cell->inline_contents;
406   cell->inline_contents.options = ts->options;
407   cell->inline_contents.text = ts->string;
408   cell->inline_contents.n_footnotes = 0;
409   cell->n_contents = 1;
410   cell->destructor = NULL;
411 }
412
413
414 static int
415 table_string_get_rule (const struct table *ts UNUSED,
416                        enum table_axis axis UNUSED, int x UNUSED, int y UNUSED)
417 {
418   return TAL_0;
419 }
420
421 static const struct table_class table_string_class =
422   {
423     table_string_destroy,
424     table_string_get_cell,
425     table_string_get_rule,
426     NULL,                       /* paste */
427     NULL,                       /* select */
428   };