X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;f=src%2Fui%2Fgui%2Fpsppire-delimited-text.c;h=a3b110494af491487172d90aac0361c9ad2b0878;hb=37c21ba9301956091823dea7d84ff0400330bd4b;hp=98f76b4301953f330f881d37b193111eb8893f52;hpb=e1c4d5d0f0c63ef20927ac2cd7f9ff4e7e4cf5c5;p=pspp diff --git a/src/ui/gui/psppire-delimited-text.c b/src/ui/gui/psppire-delimited-text.c index 98f76b4301..a3b110494a 100644 --- a/src/ui/gui/psppire-delimited-text.c +++ b/src/ui/gui/psppire-delimited-text.c @@ -21,7 +21,9 @@ #include "psppire-delimited-text.h" #include "psppire-text-file.h" +#include "language/commands/data-parser.h" #include "libpspp/str.h" +#include "libpspp/string-array.h" #include "libpspp/i18n.h" #include @@ -32,55 +34,77 @@ enum PROP_0, PROP_CHILD, PROP_DELIMITERS, + PROP_QUOTE, PROP_FIRST_LINE }; +static struct data_parser * +make_data_parser (PsppireDelimitedText *tf) +{ + struct data_parser *parser = data_parser_create (); + data_parser_set_type (parser, DP_DELIMITED); + data_parser_set_span (parser, false); + data_parser_set_quotes (parser, ss_empty ()); + data_parser_set_quote_escape (parser, true); + data_parser_set_empty_line_has_field (parser, true); + + bool space = false; + struct string hard_delimiters = DS_EMPTY_INITIALIZER; + GSList *del; + for (del = tf->delimiters; del; del = g_slist_next (del)) + { + gunichar c = GPOINTER_TO_INT (del->data); + if (c == ' ') + space = true; + else + ds_put_unichar (&hard_delimiters, c); + } + data_parser_set_soft_delimiters (parser, ss_cstr (space ? " " : "")); + data_parser_set_hard_delimiters (parser, ds_ss (&hard_delimiters)); + ds_destroy (&hard_delimiters); + + if (tf->quote) + { + struct string quote = DS_EMPTY_INITIALIZER; + ds_put_unichar ("e, tf->quote); + data_parser_set_quotes (parser, ds_ss ("e)); + ds_destroy ("e); + } + return parser; +} + static void count_delims (PsppireDelimitedText *tf) { - if (tf->child) + if (tf->child == NULL) + return; + + struct data_parser *parser = make_data_parser (tf); + + tf->max_fields = 0; + GtkTreeIter iter; + gboolean valid; + for (valid = gtk_tree_model_get_iter_first (tf->child, &iter); + valid; + valid = gtk_tree_model_iter_next (tf->child, &iter)) { - tf->max_delimiters = 0; - GtkTreeIter iter; - gboolean valid; - for (valid = gtk_tree_model_get_iter_first (tf->child, &iter); - valid; - valid = gtk_tree_model_iter_next (tf->child, &iter)) - { - // FIXME: Box these lines to avoid constant allocation/deallocation - gchar *foo = 0; - gtk_tree_model_get (tf->child, &iter, 1, &foo, -1); - { - char *line = foo; - gint count = 0; - while (*line) - { - GSList *del; - for (del = tf->delimiters; del; del = g_slist_next (del)) - { - if (*line == GPOINTER_TO_INT (del->data)) - count++; - } - line++; - } - tf->max_delimiters = MAX (tf->max_delimiters, count); - } - g_free (foo); - } + gchar *line = NULL; + gtk_tree_model_get (tf->child, &iter, 1, &line, -1); + size_t n_fields = data_parser_split (parser, ss_cstr (line), NULL); + if (n_fields > tf->max_fields) + tf->max_fields = n_fields; + g_free (line); } - // g_print ("Max Number of delimiters per row: %d\n", tf->max_delimiters); + + data_parser_destroy (parser); } static void cache_invalidate (PsppireDelimitedText *tf) { - memset (tf->cache_starts, 0, 512); - if (tf->const_cache.string) - { - ss_dealloc (&tf->const_cache); - tf->const_cache.string = NULL; - tf->cache_row = -1; - } + tf->cache_row = -1; + data_parser_destroy (tf->parser); + tf->parser = make_data_parser (tf); } static void @@ -98,11 +122,15 @@ psppire_delimited_text_set_property (GObject *object, break; case PROP_CHILD: tf->child = g_value_get_object (value); + g_return_if_fail (PSPPIRE_IS_TEXT_FILE (tf->child)); break; case PROP_DELIMITERS: g_slist_free (tf->delimiters); tf->delimiters = g_slist_copy (g_value_get_pointer (value)); break; + case PROP_QUOTE: + tf->quote = g_value_get_uint (value); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; @@ -128,21 +156,27 @@ psppire_delimited_text_get_property (GObject *object, case PROP_DELIMITERS: g_value_set_pointer (value, text_file->delimiters); break; + case PROP_QUOTE: + g_value_set_uint (value, text_file->quote); + break; default: G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); break; }; } - -static void psppire_delimited_text_init (PsppireDelimitedText *text_file); -static void psppire_delimited_text_class_init (PsppireDelimitedTextClass *class); - static void psppire_delimited_text_finalize (GObject *object); static void psppire_delimited_text_dispose (GObject *object); static GObjectClass *parent_class = NULL; +static gint +n_lines (PsppireDelimitedText *file) +{ + PsppireTextFile *child = PSPPIRE_TEXT_FILE (file->child); + + return child->maximum_lines; +} static gboolean __tree_get_iter (GtkTreeModel *tree_model, @@ -153,7 +187,6 @@ __tree_get_iter (GtkTreeModel *tree_model, if (path == NULL) return FALSE; - // g_print ("%s:%d %s %s\n", __FILE__, __LINE__, __FUNCTION__, gtk_tree_path_to_string (path)); gint *indices = gtk_tree_path_get_indices (path); @@ -162,12 +195,11 @@ __tree_get_iter (GtkTreeModel *tree_model, gint n = *indices; - gint children = gtk_tree_model_iter_n_children (file->child, NULL); + gint children = n_lines (file); if (n >= children - file->first_line) return FALSE; - // g_print ("%s:%d %s %d Children: %d\n", __FILE__, __LINE__, __FUNCTION__, n, children); iter->user_data = GINT_TO_POINTER (n); iter->stamp = file->stamp; @@ -185,9 +217,8 @@ __tree_iter_next (GtkTreeModel *tree_model, gint n = GPOINTER_TO_INT (iter->user_data); - // g_print ("%s:%d %s %d\n", __FILE__, __LINE__, __FUNCTION__, n); - gint children = gtk_tree_model_iter_n_children (file->child, NULL); + gint children = n_lines (file); if (n + 1 >= children - file->first_line) return FALSE; @@ -202,7 +233,6 @@ static GType __tree_get_column_type (GtkTreeModel *tree_model, gint index) { - // g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); if (index == 0) return G_TYPE_INT; @@ -213,7 +243,6 @@ static gboolean __iter_has_child (GtkTreeModel *tree_model, GtkTreeIter *iter) { - g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); return 0; } @@ -223,7 +252,6 @@ __iter_parent (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *child) { - g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); return 0; } @@ -231,13 +259,12 @@ static GtkTreePath * __tree_get_path (GtkTreeModel *tree_model, GtkTreeIter *iter) { - // g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model); g_return_val_if_fail (file->stamp == iter->stamp, FALSE); gint n = GPOINTER_TO_INT (iter->user_data); - gint children = gtk_tree_model_iter_n_children (file->child, NULL); + gint children = n_lines (file); if (n >= children - file->first_line) return NULL; @@ -251,7 +278,6 @@ __iter_children (GtkTreeModel *tree_model, GtkTreeIter *iter, GtkTreeIter *parent) { - g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); return 0; } @@ -261,10 +287,9 @@ __tree_model_iter_n_children (GtkTreeModel *tree_model, GtkTreeIter *iter) { PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model); - // g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); g_assert (iter == NULL); - gint children = gtk_tree_model_iter_n_children (file->child, NULL); + gint children = n_lines (file); return children - file->first_line; } @@ -272,7 +297,6 @@ __tree_model_iter_n_children (GtkTreeModel *tree_model, static GtkTreeModelFlags __tree_model_get_flags (GtkTreeModel *model) { - // g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); g_return_val_if_fail (PSPPIRE_IS_DELIMITED_TEXT (model), (GtkTreeModelFlags) 0); return GTK_TREE_MODEL_LIST_ONLY; @@ -281,11 +305,10 @@ __tree_model_get_flags (GtkTreeModel *model) static gint __tree_model_get_n_columns (GtkTreeModel *tree_model) { - // g_print ("%s:%d %s\n", __FILE__, __LINE__, __FUNCTION__); PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (tree_model); - /* + 1 for the trailing field and +1 for the leading line number column */ - return tf->max_delimiters + 1 + 1; + /* +1 for the leading line number column */ + return tf->max_fields + 1; } @@ -295,7 +318,6 @@ __iter_nth_child (GtkTreeModel *tree_model, GtkTreeIter *parent, gint n) { - // g_print ("%s:%d %s %d\n", __FILE__, __LINE__, __FUNCTION__, n); PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model); g_assert (parent == NULL); @@ -317,51 +339,19 @@ __iter_nth_child (GtkTreeModel *tree_model, return TRUE; } - /* Split row N into it's delimited fields (if it is not already cached) and set this row as the current cache. */ static void split_row_into_fields (PsppireDelimitedText *file, gint n) { if (n == file->cache_row) /* Cache hit */ - { - return; - } - - memset (file->cache_starts, 0, 512); - /* Cache miss */ - if (file->const_cache.string) - { - ss_dealloc (&file->const_cache); - } - ss_alloc_substring_pool (&file->const_cache, - PSPPIRE_TEXT_FILE (file->child)->lines[n], NULL); - struct substring cs = file->const_cache; - int field = 0; - file->cache_starts[0] = cs.string; - for (; - UINT32_MAX != ss_first_mb (cs); - ss_get_mb (&cs)) - { - ucs4_t xx = ss_first_mb (cs); - GSList *del; - for (del = file->delimiters; del; del = g_slist_next (del)) - { - if (xx == GPOINTER_TO_INT (del->data)) - { - field++; - int char_len = ss_first_mblen (cs); - file->cache_starts[field] = cs.string + char_len; - while (char_len > 0) - { - cs.string[char_len - 1] = '\0'; - char_len--; - } - break; - } - } - } + return; + if (!file->parser) + file->parser = make_data_parser (file); + string_array_clear (&file->cache); + data_parser_split (file->parser, PSPPIRE_TEXT_FILE (file->child)->lines[n], + &file->cache); file->cache_row = n; } @@ -373,7 +363,7 @@ psppire_delimited_text_get_header_title (PsppireDelimitedText *file, gint column split_row_into_fields (file, file->first_line - 1); - return file->cache_starts [column]; + return column < file->cache.n ? file->cache.strings[column] : ""; } static void @@ -382,14 +372,12 @@ __get_value (GtkTreeModel *tree_model, gint column, GValue *value) { - // g_print ("%s:%d %s Col: %d\n", __FILE__, __LINE__, __FUNCTION__, column); PsppireDelimitedText *file = PSPPIRE_DELIMITED_TEXT (tree_model); g_return_if_fail (iter->stamp == file->stamp); gint n = GPOINTER_TO_INT (iter->user_data) + file->first_line; - // g_print ("%s:%d Row: %d\n", __FILE__, __LINE__, n); if (column == 0) { @@ -402,7 +390,9 @@ __get_value (GtkTreeModel *tree_model, split_row_into_fields (file, n); - g_value_set_string (value, file->cache_starts [column - 1]); + size_t idx = column - 1; + const char *s = idx < file->cache.n ? file->cache.strings[idx] : ""; + g_value_set_string (value, s); } @@ -424,44 +414,9 @@ __tree_model_init (GtkTreeModelIface *iface) iface->iter_parent = __iter_parent; } - -GType -psppire_delimited_text_get_type (void) -{ - static GType text_file_type = 0; - - if (!text_file_type) - { - static const GTypeInfo text_file_info = - { - sizeof (PsppireDelimitedTextClass), - NULL, /* base_init */ - NULL, /* base_finalize */ - (GClassInitFunc) psppire_delimited_text_class_init, - NULL, /* class_finalize */ - NULL, /* class_data */ - sizeof (PsppireDelimitedText), - 0, - (GInstanceInitFunc) psppire_delimited_text_init, - }; - - static const GInterfaceInfo tree_model_info = { - (GInterfaceInitFunc) __tree_model_init, - NULL, - NULL - }; - - text_file_type = g_type_register_static (G_TYPE_OBJECT, - "PsppireDelimitedText", - &text_file_info, 0); - - g_type_add_interface_static (text_file_type, GTK_TYPE_TREE_MODEL, - &tree_model_info); - } - - return text_file_type; -} - +G_DEFINE_TYPE_WITH_CODE (PsppireDelimitedText, psppire_delimited_text, G_TYPE_OBJECT, + G_IMPLEMENT_INTERFACE (GTK_TYPE_TREE_MODEL, + __tree_model_init)) static void psppire_delimited_text_class_init (PsppireDelimitedTextClass *class) @@ -469,7 +424,7 @@ psppire_delimited_text_class_init (PsppireDelimitedTextClass *class) GObjectClass *object_class; parent_class = g_type_class_peek_parent (class); - object_class = (GObjectClass*) class; + object_class = G_OBJECT_CLASS (class); GParamSpec *first_line_spec = g_param_spec_int ("first-line", @@ -484,6 +439,13 @@ psppire_delimited_text_class_init (PsppireDelimitedTextClass *class) P_("A GSList of gunichars which delimit the fields."), G_PARAM_READWRITE); + GParamSpec *quote_spec = + g_param_spec_unichar ("quote", + "Quote Character", + P_("A character that quotes the field, or 0 to disable quoting."), + 0, + G_PARAM_READWRITE); + GParamSpec *child_spec = g_param_spec_object ("child", "Child Model", @@ -502,6 +464,10 @@ psppire_delimited_text_class_init (PsppireDelimitedTextClass *class) PROP_DELIMITERS, delimiters_spec); + g_object_class_install_property (object_class, + PROP_QUOTE, + quote_spec); + g_object_class_install_property (object_class, PROP_FIRST_LINE, first_line_spec); @@ -518,27 +484,26 @@ psppire_delimited_text_init (PsppireDelimitedText *text_file) text_file->first_line = 0; text_file->delimiters = g_slist_prepend (NULL, GINT_TO_POINTER (':')); - text_file->const_cache.string = NULL; - text_file->const_cache.length = 0; text_file->cache_row = -1; - memset (text_file->cache_starts, 0, 512); + string_array_init (&text_file->cache); + text_file->parser = NULL; - text_file->max_delimiters = 0; + text_file->max_fields = 0; + + text_file->quote = 0; text_file->dispose_has_run = FALSE; text_file->stamp = g_random_int (); } -GtkTreeModel * +PsppireDelimitedText * psppire_delimited_text_new (GtkTreeModel *child) { - PsppireDelimitedText *retval = + return g_object_new (PSPPIRE_TYPE_DELIMITED_TEXT, "child", child, NULL); - - return retval; } static void @@ -547,8 +512,8 @@ psppire_delimited_text_finalize (GObject *object) PsppireDelimitedText *tf = PSPPIRE_DELIMITED_TEXT (object); g_slist_free (tf->delimiters); - - ss_dealloc (&tf->const_cache); + string_array_destroy (&tf->cache); + data_parser_destroy (tf->parser); /* must chain up */ (* parent_class->finalize) (object);