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