/* PSPP - a program for statistical analysis.
- Copyright (C) 1997-9, 2000, 2006, 2007 Free Software Foundation, Inc.
+ Copyright (C) 1997-9, 2000, 2006, 2007, 2009 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
#include <stdlib.h>
#include <ctype.h>
-#include "case.h"
-#include "category.h"
-#include "settings.h"
-#include "value-labels.h"
-#include "vardict.h"
-#include "variable.h"
-#include "vector.h"
+#include <data/attributes.h>
+#include <data/case.h>
+#include <data/category.h>
+#include <data/identifier.h>
+#include <data/settings.h>
+#include <data/value-labels.h>
+#include <data/vardict.h>
+#include <data/variable.h>
+#include <data/vector.h>
#include <libpspp/array.h>
+#include <libpspp/assertion.h>
#include <libpspp/compiler.h>
#include <libpspp/hash.h>
#include <libpspp/message.h>
#include <libpspp/pool.h>
#include <libpspp/str.h>
+#include "intprops.h"
#include "minmax.h"
#include "xalloc.h"
{
struct variable **var; /* Variables. */
size_t var_cnt, var_cap; /* Number of variables, capacity. */
+ struct caseproto *proto; /* Prototype for dictionary cases
+ (updated lazily). */
struct hsh_table *name_tab; /* Variable index by name. */
int next_value_idx; /* Index of next `union value' to allocate. */
const struct variable **split; /* SPLIT FILE vars. */
struct string documents; /* Documents, as a string. */
struct vector **vector; /* Vectors of variables. */
size_t vector_cnt; /* Number of vectors. */
+ struct attrset attributes; /* Custom attributes. */
+
+ char *encoding; /* Character encoding of string data */
+
const struct dict_callbacks *callbacks; /* Callbacks on dictionary
modification */
void *cb_data ; /* Data passed to callbacks */
+
+ void (*changed) (struct dictionary *, void *); /* Generic change callback */
+ void *changed_data;
};
+
+void
+dict_set_encoding (struct dictionary *d, const char *enc)
+{
+ if (enc)
+ d->encoding = xstrdup (enc);
+}
+
+const char *
+dict_get_encoding (const struct dictionary *d)
+{
+ return d->encoding ;
+}
+
+
+void
+dict_set_change_callback (struct dictionary *d,
+ void (*changed) (struct dictionary *, void*),
+ void *data)
+{
+ d->changed = changed;
+ d->changed_data = data;
+}
+
+/* Discards dictionary D's caseproto. (It will be regenerated
+ lazily, on demand.) */
+static void
+invalidate_proto (struct dictionary *d)
+{
+ caseproto_unref (d->proto);
+ d->proto = NULL;
+}
+
/* Print a representation of dictionary D to stdout, for
debugging purposes. */
void
{
const struct variable *v =
d->var[i];
- printf ("Name: %s;\tdict_idx: %d; case_idx: %d\n",
+ printf ("Name: %s;\tdict_idx: %zu; case_idx: %zu\n",
var_get_name (v),
var_get_dict_index (v),
var_get_case_index (v));
d->name_tab = hsh_create (8, compare_vars_by_name, hash_var_by_name,
NULL, NULL);
+ attrset_init (&d->attributes);
return d;
}
for (i = 0; i < s->vector_cnt; i++)
d->vector[i] = vector_clone (s->vector[i], s, d);
+ if ( s->encoding)
+ d->encoding = xstrdup (s->encoding);
+
+ dict_set_attributes (d, dict_get_attributes (s));
+
return d;
}
free (d->var);
d->var = NULL;
d->var_cnt = d->var_cap = 0;
+ invalidate_proto (d);
hsh_clear (d->name_tab);
d->next_value_idx = 0;
dict_set_split_vars (d, NULL, 0);
d->label = NULL;
ds_destroy (&d->documents);
dict_clear_vectors (d);
+ attrset_clear (&d->attributes);
}
/* Destroys the aux data for every variable in D, by calling
dict_clear (d);
hsh_destroy (d->name_tab);
+ attrset_destroy (&d->attributes);
free (d);
}
}
d->var[d->var_cnt++] = v;
hsh_force_insert (d->name_tab, v);
+ if ( d->changed ) d->changed (d, d->changed_data);
if ( d->callbacks && d->callbacks->var_added )
d->callbacks->var_added (d, var_get_dict_index (v), d->cb_data);
- d->next_value_idx += var_get_value_cnt (v);
+ d->next_value_idx++;
+ invalidate_proto (d);
return v;
}
vdi.dict_index = dict_index;
var_set_vardict (v, &vdi);
+ if ( d->changed ) d->changed (d, d->changed_data);
if ( d->callbacks && d->callbacks->var_changed )
d->callbacks->var_changed (d, dict_index, d->cb_data);
}
{
int dict_index = var_get_dict_index (v);
const int case_index = var_get_case_index (v);
- const int val_cnt = var_get_value_cnt (v);
+ const int width = var_get_width (v);
assert (dict_contains_var (d, v));
var_clear_vardict (v);
var_destroy (v);
+ if ( d->changed ) d->changed (d, d->changed_data);
+ invalidate_proto (d);
if (d->callbacks && d->callbacks->var_deleted )
- d->callbacks->var_deleted (d, dict_index, case_index, val_cnt, d->cb_data);
+ d->callbacks->var_deleted (d, dict_index, case_index, width, d->cb_data);
}
/* Deletes the COUNT variables listed in VARS from D. This is
if (settings_get_algorithm () == ENHANCED)
var_clear_short_names (v);
+ if ( d->changed ) d->changed (d, d->changed_data);
if ( d->callbacks && d->callbacks->var_changed )
d->callbacks->var_changed (d, var_get_dict_index (v), d->cb_data);
}
return true;
}
+/* Returns true if a variable named NAME may be inserted in DICT;
+ that is, if there is not already a variable with that name in
+ DICT and if NAME is not a reserved word. (The caller's checks
+ have already verified that NAME is otherwise acceptable as a
+ variable name.) */
+static bool
+var_name_is_insertable (const struct dictionary *dict, const char *name)
+{
+ return (dict_lookup_var (dict, name) == NULL
+ && lex_id_to_token (ss_cstr (name)) == T_ID);
+}
+
+static bool
+make_hinted_name (const struct dictionary *dict, const char *hint,
+ char name[VAR_NAME_LEN + 1])
+{
+ bool dropped = false;
+ char *cp;
+
+ for (cp = name; *hint && cp < name + VAR_NAME_LEN; hint++)
+ {
+ if (cp == name
+ ? lex_is_id1 (*hint) && *hint != '$'
+ : lex_is_idn (*hint))
+ {
+ if (dropped)
+ {
+ *cp++ = '_';
+ dropped = false;
+ }
+ if (cp < name + VAR_NAME_LEN)
+ *cp++ = *hint;
+ }
+ else if (cp > name)
+ dropped = true;
+ }
+ *cp = '\0';
+
+ if (name[0] != '\0')
+ {
+ size_t len = strlen (name);
+ unsigned long int i;
+
+ if (var_name_is_insertable (dict, name))
+ return true;
+
+ for (i = 0; i < ULONG_MAX; i++)
+ {
+ char suffix[INT_BUFSIZE_BOUND (i) + 1];
+ int ofs;
+
+ suffix[0] = '_';
+ if (!str_format_26adic (i + 1, &suffix[1], sizeof suffix - 1))
+ NOT_REACHED ();
+
+ ofs = MIN (VAR_NAME_LEN - strlen (suffix), len);
+ strcpy (&name[ofs], suffix);
+
+ if (var_name_is_insertable (dict, name))
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool
+make_numeric_name (const struct dictionary *dict, unsigned long int *num_start,
+ char name[VAR_NAME_LEN + 1])
+{
+ unsigned long int number;
+
+ for (number = num_start != NULL ? MAX (*num_start, 1) : 1;
+ number < ULONG_MAX;
+ number++)
+ {
+ sprintf (name, "VAR%03lu", number);
+ if (dict_lookup_var (dict, name) == NULL)
+ {
+ if (num_start != NULL)
+ *num_start = number + 1;
+ return true;
+ }
+ }
+
+ if (num_start != NULL)
+ *num_start = ULONG_MAX;
+ return false;
+}
+
+
+/* Attempts to devise a variable name unique within DICT.
+ Returns true if successful, in which case the new variable
+ name is stored into NAME. Returns false if all names that can
+ be generated have already been taken. (Returning false is
+ quite unlikely: at least ULONG_MAX unique names can be
+ generated.)
+
+ HINT, if it is non-null, is used as a suggestion that will be
+ modified for suitability as a variable name and for
+ uniqueness.
+
+ If HINT is null or entirely unsuitable, a name in the form
+ "VAR%03d" will be generated, where the smallest unused integer
+ value is used. If NUM_START is non-null, then its value is
+ used as the minimum numeric value to check, and it is updated
+ to the next value to be checked.
+ */
+bool
+dict_make_unique_var_name (const struct dictionary *dict, const char *hint,
+ unsigned long int *num_start,
+ char name[VAR_NAME_LEN + 1])
+{
+ return ((hint != NULL && make_hinted_name (dict, hint, name))
+ || make_numeric_name (dict, num_start, name));
+}
+
/* Returns the weighting variable in dictionary D, or a null
pointer if the dictionary is unweighted. */
struct variable *
d->weight = v;
+ if (d->changed) d->changed (d, d->changed_data);
if ( d->callbacks && d->callbacks->weight_changed )
d->callbacks->weight_changed (d,
v ? var_get_dict_index (v) : -1,
d->filter = v;
+ if (d->changed) d->changed (d, d->changed_data);
if ( d->callbacks && d->callbacks->filter_changed )
d->callbacks->filter_changed (d,
v ? var_get_dict_index (v) : -1,
d->case_limit = case_limit;
}
+/* Returns the prototype used for cases created by dictionary D. */
+const struct caseproto *
+dict_get_proto (const struct dictionary *d_)
+{
+ struct dictionary *d = (struct dictionary *) d_;
+ if (d->proto == NULL)
+ {
+ size_t i;
+
+ d->proto = caseproto_create ();
+ d->proto = caseproto_reserve (d->proto, d->var_cnt);
+ for (i = 0; i < d->var_cnt; i++)
+ d->proto = caseproto_set_width (d->proto,
+ var_get_case_index (d->var[i]),
+ var_get_width (d->var[i]));
+ }
+ return d->proto;
+}
+
/* Returns the case index of the next value to be added to D.
This value is the number of `union value's that need to be
allocated to store a case for dictionary D. */
for (i = 0; i < d->var_cnt; i++)
{
struct variable *v = d->var[i];
- set_var_case_index (v, d->next_value_idx);
- d->next_value_idx += var_get_value_cnt (v);
- }
-}
-
-/*
- Reassigns case indices for D, increasing each index above START by
- the value PADDING.
-*/
-static void
-dict_pad_values (struct dictionary *d, int start, int padding)
-{
- size_t i;
-
- if ( padding <= 0 )
- return;
-
- for (i = 0; i < d->var_cnt; ++i)
- {
- struct variable *v = d->var[i];
-
- int index = var_get_case_index (v);
-
- if ( index >= start)
- set_var_case_index (v, index + padding);
+ set_var_case_index (v, d->next_value_idx++);
}
-
- d->next_value_idx += padding;
+ invalidate_proto (d);
}
-
/* Returns the number of values occupied by the variables in
dictionary D. All variables are considered if EXCLUDE_CLASSES
is 0, or it may contain one or more of (1u << DC_ORDINARY),
{
enum dict_class class = var_get_dict_class (d->var[i]);
if (!(exclude_classes & (1u << class)))
- cnt += var_get_value_cnt (d->var[i]);
+ cnt++;
}
return cnt;
}
+
+/* Returns the case prototype that would result after deleting
+ all variables from D that are not in one of the
+ EXCLUDE_CLASSES and compacting the dictionary with
+ dict_compact().
+
+ The caller must unref the returned caseproto when it is no
+ longer needed. */
+struct caseproto *
+dict_get_compacted_proto (const struct dictionary *d,
+ unsigned int exclude_classes)
+{
+ struct caseproto *proto;
+ size_t i;
+
+ assert ((exclude_classes & ~((1u << DC_ORDINARY)
+ | (1u << DC_SYSTEM)
+ | (1u << DC_SCRATCH))) == 0);
+
+ proto = caseproto_create ();
+ for (i = 0; i < d->var_cnt; i++)
+ {
+ struct variable *v = d->var[i];
+ if (!(exclude_classes & (1u << var_get_dict_class (v))))
+ proto = caseproto_add_width (proto, var_get_width (v));
+ }
+ return proto;
+}
\f
/* Returns the SPLIT FILE vars (see cmd_split_file()). Call
dict_get_split_cnt() to determine how many SPLIT FILE vars
&v, compare_var_ptrs, NULL);
if (orig_count != d->split_cnt)
{
+ if (d->changed) d->changed (d, d->changed_data);
/* We changed the set of split variables so invoke the
callback. */
if (d->callbacks && d->callbacks->split_changed)
d->split = NULL;
}
+ if (d->changed) d->changed (d, d->changed_data);
if ( d->callbacks && d->callbacks->split_changed )
d->callbacks->split_changed (d, d->cb_data);
}
msg (SW, _("Truncating document line to %d bytes."), DOC_LINE_LENGTH);
}
buf_copy_str_rpad (ds_put_uninit (&d->documents, DOC_LINE_LENGTH),
- DOC_LINE_LENGTH, line);
+ DOC_LINE_LENGTH, line, ' ');
}
/* Returns the number of document lines in dictionary D. */
d->vector_cnt = 0;
}
+/* Returns D's attribute set. The caller may examine or modify
+ the attribute set, but must not destroy it. Destroying D or
+ calling dict_set_attributes for D will also destroy D's
+ attribute set. */
+struct attrset *
+dict_get_attributes (const struct dictionary *d)
+{
+ return (struct attrset *) &d->attributes;
+}
+
+/* Replaces D's attributes set by a copy of ATTRS. */
+void
+dict_set_attributes (struct dictionary *d, const struct attrset *attrs)
+{
+ attrset_destroy (&d->attributes);
+ attrset_clone (&d->attributes, attrs);
+}
+
+/* Returns true if D has at least one attribute in its attribute
+ set, false if D's attribute set is empty. */
+bool
+dict_has_attributes (const struct dictionary *d)
+{
+ return attrset_count (&d->attributes) > 0;
+}
+
/* Called from variable.c to notify the dictionary that some property of
the variable has changed */
void
const struct vardict_info *vdi = var_get_vardict (v);
struct dictionary *d = vdi->dict;
+ if ( NULL == d)
+ return;
+
+ if (d->changed ) d->changed (d, d->changed_data);
if ( d->callbacks && d->callbacks->var_changed )
d->callbacks->var_changed (d, var_get_dict_index (v), d->cb_data);
}
/* Called from variable.c to notify the dictionary that the variable's width
has changed */
void
-dict_var_resized (const struct variable *v, int delta)
+dict_var_resized (const struct variable *v, int old_width)
{
if ( var_has_vardict (v))
{
d = vdi->dict;
- dict_pad_values (d, var_get_case_index(v) + 1, delta);
+ if (d->changed) d->changed (d, d->changed_data);
+ invalidate_proto (d);
if ( d->callbacks && d->callbacks->var_resized )
- d->callbacks->var_resized (d, var_get_dict_index (v), delta, d->cb_data);
+ d->callbacks->var_resized (d, var_get_dict_index (v), old_width,
+ d->cb_data);
}
}
+
+/* Called from variable.c to notify the dictionary that the variable's display width
+ has changed */
+void
+dict_var_display_width_changed (const struct variable *v)
+{
+ if ( var_has_vardict (v))
+ {
+ const struct vardict_info *vdi = var_get_vardict (v);
+ struct dictionary *d;
+
+ d = vdi->dict;
+
+ if (d->changed) d->changed (d, d->changed_data);
+ if ( d->callbacks && d->callbacks->var_display_width_changed )
+ d->callbacks->var_display_width_changed (d, var_get_dict_index (v), d->cb_data);
+ }
+}
+