work
authorBen Pfaff <blp@cs.stanford.edu>
Wed, 30 Dec 2020 20:29:13 +0000 (12:29 -0800)
committerBen Pfaff <blp@cs.stanford.edu>
Thu, 31 Dec 2020 03:58:11 +0000 (19:58 -0800)
src/output/pivot-table.c
src/output/pivot-table.h
tests/automake.mk
tests/output/pivot-table-test.c [new file with mode: 0644]

index d0588365a143529ba2812fcc31a0918f21bc86d9..561e45657d2cbd0dc41f1a8059fed475b38b64a5 100644 (file)
@@ -396,6 +396,10 @@ pivot_category_set_rc (struct pivot_category *category, const char *s)
     category->dimension->table, s);
   if (format)
     category->format = *format;
+
+  struct pivot_value *name = category->name;
+  if (name->type == PIVOT_VALUE_NUMERIC && !name->numeric.format.w)
+    name->numeric.format = format ? *format : *settings_get_format ();
 }
 
 static void
@@ -789,6 +793,12 @@ pivot_result_class_change (const char *s_, const struct fmt_spec *format)
 
   return rc != NULL;
 }
+
+bool
+is_pivot_result_class (const char *s)
+{
+  return pivot_result_class_find (s) != NULL;
+}
 \f
 /* Pivot tables. */
 
@@ -1368,8 +1378,8 @@ pivot_category_assign_label_depth (struct pivot_category *category,
 
 static bool
 pivot_axis_assign_label_depth (struct pivot_table *table,
-                             enum pivot_axis_type axis_type,
-                             bool dimension_labels_in_corner)
+                               enum pivot_axis_type axis_type,
+                               bool dimension_labels_in_corner)
 {
   struct pivot_axis *axis = &table->axes[axis_type];
   bool any_label_shown_in_corner = false;
@@ -1401,10 +1411,6 @@ pivot_table_assign_label_depth (struct pivot_table *table)
   pivot_axis_assign_label_depth (table, PIVOT_AXIS_LAYER, false);
 }
 \f
-/* Footnotes. */
-
-\f
-\f
 static void
 indent (int indentation)
 {
index 7c36c43b2d61df2c1921bcee754975f99cf77858..22995da74c3d4cdf275d524881dc1aa752f62abf 100644 (file)
@@ -365,6 +365,7 @@ void pivot_category_destroy (struct pivot_category *);
 #define PIVOT_RC_COUNT ("RC_COUNT")
 
 bool pivot_result_class_change (const char *, const struct fmt_spec *);
+bool is_pivot_result_class (const char *);
 \f
 /* Styling for a pivot table.
 
index c90565c1244e3ce91c6f507e8d3ef11c09f998c0..e87363e4bbdb454b6c7ad35cae78e05d41272088 100644 (file)
@@ -239,9 +239,10 @@ tests_math_chart_get_ticks_format_test_LDADD = \
 check_PROGRAMS += tests/output/pivot-table-test
 tests_output_pivot_table_test_SOURCES = tests/output/pivot-table-test.c
 tests_output_pivot_table_test_LDADD = \
-       src/libpspp/liblibpspp.la \
+       src/libpspp.la \
        src/libpspp-core.la \
-       gl/libgl.la
+       gl/libgl.la \
+       $(CAIRO_LIBS)
 
 EXTRA_DIST += tests/output/render-test.c
 
diff --git a/tests/output/pivot-table-test.c b/tests/output/pivot-table-test.c
new file mode 100644 (file)
index 0000000..8200c18
--- /dev/null
@@ -0,0 +1,380 @@
+/* PSPP - a program for statistical analysis.
+   Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014 Free Software Foundation, Inc.
+
+   This program is free software: you can redistribute it and/or modify
+   it under the terms of the GNU General Public License as published by
+   the Free Software Foundation, either version 3 of the License, or
+   (at your option) any later version.
+
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   GNU General Public License for more details.
+
+   You should have received a copy of the GNU General Public License
+   along with this program.  If not, see <http://www.gnu.org/licenses/>. */
+
+#include <config.h>
+
+#include <errno.h>
+#include <getopt.h>
+#include <limits.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "data/file-handle-def.h"
+#include "language/lexer/lexer.h"
+#include "libpspp/assertion.h"
+#include "libpspp/compiler.h"
+#include "libpspp/i18n.h"
+#include "libpspp/string-map.h"
+#include "output/driver.h"
+#include "output/pivot-table.h"
+#include "output/table-item.h"
+
+#include "gl/error.h"
+#include "gl/progname.h"
+#include "gl/xalloc.h"
+#include "gl/xvasprintf.h"
+
+/* --emphasis: Enable emphasis in ASCII driver? */
+static bool emphasis;
+
+/* --box: ASCII driver box option. */
+static char *box;
+
+/* -o, --output: Base name for output files. */
+static const char *output_base = "render";
+
+static const char *parse_options (int argc, char **argv);
+static void usage (void) NO_RETURN;
+static struct pivot_table *read_table (struct lexer *);
+
+int
+main (int argc, char **argv)
+{
+  const char *input_file_name;
+
+  set_program_name (argv[0]);
+  i18n_init ();
+  output_engine_push ();
+  input_file_name = parse_options (argc, argv);
+
+  settings_init ();
+
+  struct lex_reader *reader = lex_reader_for_file (input_file_name, NULL,
+                                                   LEX_SYNTAX_AUTO,
+                                                   LEX_ERROR_CONTINUE);
+  if (!reader)
+    exit (1);
+
+  struct lexer *lexer = lex_create ();
+  lex_include (lexer, reader);
+  lex_get (lexer);
+
+  for (;;)
+    {
+      while (lex_match (lexer, T_ENDCMD))
+        continue;
+      if (lex_match (lexer, T_STOP))
+        break;
+
+      struct pivot_table *pt = read_table (lexer);
+      pivot_table_dump (pt, 0);
+      pivot_table_submit (pt);
+    }
+
+  lex_destroy (lexer);
+  output_engine_pop ();
+  fh_done ();
+
+  return 0;
+}
+
+static void PRINTF_FORMAT (2, 3)
+register_driver (struct string_map *options,
+                 const char *output_file, ...)
+{
+  va_list args;
+  va_start (args, output_file);
+  string_map_insert_nocopy (options, xstrdup ("output-file"),
+                            xvasprintf (output_file, args));
+  va_end (args);
+
+  struct output_driver *driver = output_driver_create (options);
+  if (driver == NULL)
+    exit (EXIT_FAILURE);
+  output_driver_register (driver);
+}
+
+static void
+configure_drivers (int width, int length, int min_break)
+{
+  /* Render to stdout. */
+  struct string_map options = STRING_MAP_INITIALIZER (options);
+  string_map_insert (&options, "format", "txt");
+  string_map_insert_nocopy (&options, xstrdup ("width"),
+                            xasprintf ("%d", width));
+  if (min_break >= 0)
+    string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
+                              xasprintf ("%d", min_break));
+  string_map_insert (&options, "emphasis", emphasis ? "true" : "false");
+  if (box != NULL)
+    string_map_insert (&options, "box", box);
+  register_driver (&options, "-");
+
+  /* Render to <base>.txt. */
+  register_driver (&options, "%s.txt", output_base);
+
+#ifdef HAVE_CAIRO
+  /* Render to <base>.pdf. */
+  string_map_insert (&options, "top-margin", "0");
+  string_map_insert (&options, "bottom-margin", "0");
+  string_map_insert (&options, "left-margin", "0");
+  string_map_insert (&options, "right-margin", "0");
+  string_map_insert_nocopy (&options, xstrdup ("paper-size"),
+                            xasprintf ("%dx%dpt", width * 5, length * 8));
+  if (min_break >= 0)
+    {
+      string_map_insert_nocopy (&options, xstrdup ("min-hbreak"),
+                                xasprintf ("%d", min_break * 5));
+      string_map_insert_nocopy (&options, xstrdup ("min-vbreak"),
+                                xasprintf ("%d", min_break * 8));
+    }
+  register_driver (&options, "%s.pdf", output_base);
+#endif
+
+  /* Render to <base>.csv. */
+  register_driver (&options, "%s.csv", output_base);
+
+  /* Render to <base>.odt. */
+  register_driver (&options, "%s.odt", output_base);
+
+  string_map_destroy (&options);
+}
+
+static const char *
+parse_options (int argc, char **argv)
+{
+  int width = 79;
+  int length = 66;
+  int min_break = -1;
+
+  for (;;)
+    {
+      enum {
+        OPT_WIDTH = UCHAR_MAX + 1,
+        OPT_LENGTH,
+        OPT_MIN_BREAK,
+        OPT_EMPHASIS,
+        OPT_BOX,
+        OPT_HELP
+      };
+      static const struct option options[] =
+        {
+          {"width", required_argument, NULL, OPT_WIDTH},
+          {"length", required_argument, NULL, OPT_LENGTH},
+          {"min-break", required_argument, NULL, OPT_MIN_BREAK},
+          {"emphasis", no_argument, NULL, OPT_EMPHASIS},
+          {"box", required_argument, NULL, OPT_BOX},
+          {"output", required_argument, NULL, 'o'},
+          {"help", no_argument, NULL, OPT_HELP},
+          {NULL, 0, NULL, 0},
+        };
+
+      int c = getopt_long (argc, argv, "o:", options, NULL);
+      if (c == -1)
+        break;
+
+      switch (c)
+        {
+        case OPT_WIDTH:
+          width = atoi (optarg);
+          break;
+
+        case OPT_LENGTH:
+          length = atoi (optarg);
+          break;
+
+        case OPT_MIN_BREAK:
+          min_break = atoi (optarg);
+          break;
+
+        case OPT_EMPHASIS:
+          emphasis = true;
+          break;
+
+        case OPT_BOX:
+          box = optarg;
+          break;
+
+        case 'o':
+          output_base = optarg;
+          break;
+
+        case OPT_HELP:
+          usage ();
+
+        case 0:
+          break;
+
+        case '?':
+          exit(EXIT_FAILURE);
+          break;
+
+        default:
+          NOT_REACHED ();
+        }
+
+    }
+
+  configure_drivers (width, length, min_break);
+
+  if (optind + 1 != argc)
+    error (1, 0, "exactly one non-option argument required; "
+           "use --help for help");
+  return argv[optind];
+}
+
+static void
+usage (void)
+{
+  printf ("%s, to test rendering of PSPP tables\n"
+          "usage: %s [OPTIONS] INPUT\n"
+          "\nOptions:\n"
+          "  --width=WIDTH   set page width in characters\n"
+          "  --length=LINE   set page length in lines\n",
+          program_name, program_name);
+  exit (EXIT_SUCCESS);
+}
+
+static struct pivot_value *
+read_value (struct lexer *lexer)
+{
+  struct pivot_value *value;
+  if (lex_is_number (lexer))
+    {
+      value = pivot_value_new_number (lex_number (lexer));
+      lex_get (lexer);
+    }
+  else if (lex_is_string (lexer))
+    {
+      value = pivot_value_new_user_text (lex_tokcstr (lexer), SIZE_MAX);
+      lex_get (lexer);
+    }
+  else
+    {
+      msg (SE, "Expecting pivot_value");
+      exit (1);
+    }
+
+  if (!lex_match (lexer, T_LBRACK))
+    return value;
+  do
+    {
+      msg (SE, "Options not yet supported");
+      exit (1);
+    }
+  while (lex_match (lexer, T_COMMA));
+  if (!lex_force_match (lexer, T_RBRACK))
+    exit (1);
+}
+
+static void
+read_group (struct lexer *lexer, struct pivot_table *pt,
+            struct pivot_category *group)
+{
+  if (lex_match (lexer, T_ASTERISK))
+    group->show_label = true;
+
+  if (!lex_force_match (lexer, T_LPAREN))
+    exit (1);
+
+  if (lex_match (lexer, T_RPAREN))
+    return;
+
+  do
+    {
+      struct pivot_value *name = read_value (lexer);
+      if (lex_token (lexer) == T_ASTERISK
+          || lex_token (lexer) == T_LPAREN)
+        read_group (lexer, pt, pivot_category_create_group__ (group, name));
+      else
+        {
+          char *rc;
+          if (lex_token (lexer) == T_ID
+              && is_pivot_result_class (lex_tokcstr (lexer)))
+            {
+              rc = xstrdup (lex_tokcstr (lexer));
+              lex_get (lexer);
+            }
+          else
+            rc = NULL;
+
+          pivot_category_create_leaf_rc (group, name, rc);
+
+          free (rc);
+        }
+    }
+  while (lex_match (lexer, T_COMMA));
+  if (!lex_force_match (lexer, T_RPAREN))
+    exit (1);
+}
+
+static void
+read_dimension (struct lexer *lexer, struct pivot_table *pt,
+                enum pivot_axis_type a)
+{
+  lex_match (lexer, T_EQUALS);
+
+  struct pivot_value *name = read_value (lexer);
+  struct pivot_dimension *dim = pivot_dimension_create__ (pt, a, name);
+  read_group (lexer, pt, dim->root);
+}
+
+static void
+read_look (struct lexer *lexer, struct pivot_table *pt)
+{
+  lex_match (lexer, T_EQUALS);
+
+  if (lex_is_string (lexer))
+    {
+      struct pivot_table_look *look;
+      char *error = pivot_table_look_read (lex_tokcstr (lexer), &look);
+      if (error)
+        {
+          msg (SE, "%s", error);
+          exit (1);
+        }
+
+      pivot_table_set_look (pt, look);
+      pivot_table_look_unref (look);
+    }
+}
+
+static struct pivot_table *
+read_table (struct lexer *lexer)
+{
+  struct pivot_table *pt = pivot_table_create__ (NULL, NULL);
+  while (lex_match (lexer, T_SLASH))
+    {
+      if (lex_match_id (lexer, "ROW"))
+        read_dimension (lexer, pt, PIVOT_AXIS_ROW);
+      else if (lex_match_id (lexer, "COLUMN"))
+        read_dimension (lexer, pt, PIVOT_AXIS_COLUMN);
+      else if (lex_match_id (lexer, "LAYER"))
+        read_dimension (lexer, pt, PIVOT_AXIS_LAYER);
+      else if (lex_match_id (lexer, "LOOK"))
+        read_look (lexer, pt);
+      else
+        {
+          msg (SE, "Expecting keyword");
+          exit (1);
+        }
+    }
+
+  if (!lex_force_match (lexer, T_ENDCMD))
+    exit (1);
+  return pt;
+}