output: Implement nested tables.
[pspp] / src / output / table-stomp.c
1 /* PSPP - a program for statistical analysis.
2    Copyright (C) 2009, 2011, 2013, 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 <string.h>
20
21 #include "libpspp/assertion.h"
22 #include "libpspp/tower.h"
23 #include "output/table-provider.h"
24
25 #include "gl/minmax.h"
26 #include "gl/xalloc.h"
27
28 /* This file uses TABLE_HORZ and TABLE_VERT enough to warrant abbreviating. */
29 #define H TABLE_HORZ
30 #define V TABLE_VERT
31
32 struct table_stomp
33   {
34     struct table table;
35     struct table *subtable;
36   };
37
38 static const struct table_class table_stomp_class;
39
40 static struct table_stomp *
41 table_stomp_cast (const struct table *table)
42 {
43   assert (table->klass == &table_stomp_class);
44   return UP_CAST (table, struct table_stomp, table);
45 }
46
47 /* Returns a new table based on SUBTABLE with exactly one row.  Each cell in
48    that row consists of the contents of all of the rows stacked together into a
49    single cell.  So, for example, if SUBTABLE has one column and three rows,
50    then the returned table has one column and one row, and the single cell in
51    the returned table has all of the content of the three cells in
52    SUBTABLE.
53
54    SUBTABLE should have the same column structure in every row, i.e. don't
55    stomp a table that has rows with differently joined cells. */
56 struct table *
57 table_stomp (struct table *subtable)
58 {
59   struct table_stomp *ts;
60
61   if (subtable->n[V] == 1)
62     return subtable;
63
64   ts = xmalloc (sizeof *ts);
65   table_init (&ts->table, &table_stomp_class);
66   ts->table.n[H] = subtable->n[H];
67   ts->table.n[V] = 1;
68   ts->subtable = subtable;
69   return &ts->table;
70 }
71
72 static void
73 table_stomp_destroy (struct table *t)
74 {
75   struct table_stomp *ts = table_stomp_cast (t);
76
77   table_unref (ts->subtable);
78   free (ts);
79 }
80
81 struct table_stomp_subcells
82   {
83     struct cell_contents *contents;
84
85     size_t n_subcells;
86     struct table_cell subcells[];
87   };
88
89 static void
90 table_stomp_free_cell (void *sc_)
91 {
92   struct table_stomp_subcells *sc = sc_;
93   size_t i;
94
95   for (i = 0; i < sc->n_subcells; i++)
96     table_cell_free (&sc->subcells[i]);
97   free (sc->contents);
98   free (sc);
99 }
100
101 static void
102 table_stomp_get_cell (const struct table *t, int x, int y UNUSED,
103                       struct table_cell *cell)
104 {
105   struct table_stomp *ts = table_stomp_cast (t);
106   size_t n_rows = ts->subtable->n[V];
107   struct table_stomp_subcells *sc;
108   size_t row;
109   size_t ofs;
110   size_t i;
111
112   sc = xzalloc (sizeof *sc + n_rows * sizeof *sc->subcells);
113   sc->n_subcells = 0;
114
115   cell->n_contents = 0;
116   for (row = 0; row < n_rows; )
117     {
118       struct table_cell *subcell = &sc->subcells[sc->n_subcells++];
119
120       table_get_cell (ts->subtable, x, row, subcell);
121       cell->n_contents += subcell->n_contents;
122       row = subcell->d[V][1];
123     }
124
125   cell->d[H][0] = sc->subcells[0].d[H][0];
126   cell->d[V][0] = 0;
127   cell->d[H][1] = sc->subcells[0].d[H][1];
128   cell->d[V][1] = 1;
129
130   sc->contents = xmalloc (cell->n_contents * sizeof *cell->contents);
131   cell->contents = sc->contents;
132
133   ofs = 0;
134   for (i = 0; i < sc->n_subcells; i++)
135     {
136       struct table_cell *subcell = &sc->subcells[i];
137
138       memcpy (&sc->contents[ofs], subcell->contents,
139               subcell->n_contents * sizeof *subcell->contents);
140       ofs += subcell->n_contents;
141     }
142
143   cell->destructor = table_stomp_free_cell;
144   cell->destructor_aux = sc;
145 }
146
147 static int
148 table_stomp_get_rule (const struct table *t,
149                       enum table_axis axis, int x, int y)
150 {
151   struct table_stomp *ts = table_stomp_cast (t);
152
153   return table_get_rule (ts->subtable, axis, x,
154                          axis == H || y == 0 ? y : ts->subtable->n[V]);
155 }
156
157 static const struct table_class table_stomp_class =
158   {
159     table_stomp_destroy,
160     table_stomp_get_cell,
161     table_stomp_get_rule,
162     NULL,                       /* paste */
163     NULL,                       /* select */
164   };