/* * Copyright (c) 2003 by Emmanuele Bassi (see the file AUTHORS) * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 of the License, or (at your option) any later version. * * This library 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public * License along with this library; if not, write to the * Free Software Foundation, Inc., 59 Temple Place - Suite 330, * Boston, MA 02111-1307 USA. */ #include "gconfperl.h" /* * GConfValue is a dynamic type container used by GConf, in many ways similar * to GValue. It should be accessed only via methods, so GConf doesn't export * it as a registered type. Hence, I decided to translate it into a Perl data * structure completely transparent to the programmer; GConfValues are used * only internally: what a programmer will always see or use will be an hashref * containing the "type" field, which is used to store the symbolic type of the * key which GConfValue is bound to, and the payload, that is the value bound * to the key. Fundamental types will have the "value" field filled with the * corresponding perl scalar; lists of fundamental types will have the "value" * field filled with an arrayref of scalars; pairs of fundamental types will * have two fields, "car" (for the first value of the pair) and "cdr" (for the * second value of the pair), each one containing an hashref corresponding to a * GConfValue of their fundamental types. This may seems a little obfuscated, * but, since we're not providing any accessor methods to gather the data * inside GConfValue, I've decided to keep the semantics as much as similar to * the corresponding C one. (ebassi) * */ /* gconfperl_sv_from_value returns the correct SV for the fundamental types * stored inside a GConfValue. gconfperl_value_from_sv is used the other way * around, to fill an already initialized GConfValue from a SV. */ static SV * gconfperl_sv_from_value (GConfValue * v) { SV * sv; switch (v->type) { case GCONF_VALUE_BOOL: sv = newSViv (gconf_value_get_bool (v)); break; case GCONF_VALUE_FLOAT: sv = newSVnv (gconf_value_get_float (v)); break; case GCONF_VALUE_INT: sv = newSViv (gconf_value_get_int (v)); break; case GCONF_VALUE_STRING: sv = newSVGChar (gconf_value_get_string (v)); break; case GCONF_VALUE_SCHEMA: sv = newSVGConfSchema (gconf_value_get_schema (v)); break; case GCONF_VALUE_INVALID: default: sv = NULL; break; } return sv; } static void gconfperl_value_from_sv (SV * sv, GConfValue * v) { switch (v->type) { case GCONF_VALUE_BOOL: gconf_value_set_bool (v, SvIV (sv)); break; case GCONF_VALUE_FLOAT: gconf_value_set_float (v, SvNV (sv)); break; case GCONF_VALUE_INT: gconf_value_set_int (v, SvIV (sv)); break; case GCONF_VALUE_STRING: gconf_value_set_string (v, SvGChar (sv)); break; case GCONF_VALUE_SCHEMA: gconf_value_set_schema (v, SvGConfSchema (sv)); break; case GCONF_VALUE_INVALID: default: break; } } /* * Create a SV from a GConfValue. * The hash has this form: * * fundamentals = { 'int' | 'bool' | 'float' | 'string' | 'schema' } * * iff := * { type => , value => { | } } * * iff := 'pair' * { * type => 'pair', * car => { type => , value => }, * cdr => { type => , value => } * } * * a schema is a fundamental type because we have a type for it, like * we do have types for integer, boolean, floating and string values. */ SV * newSVGConfValue (GConfValue * v) { HV * h; SV * sv; HV * stash; if (! v) return newSVsv(&PL_sv_undef); h = newHV (); sv = newRV_noinc ((SV *) h); /* safe */ switch (v->type) { case GCONF_VALUE_STRING: case GCONF_VALUE_INT: case GCONF_VALUE_FLOAT: case GCONF_VALUE_BOOL: case GCONF_VALUE_SCHEMA: /* these are fundamental types, so store type and value * directly inside the hashref; for the type, use the * 'stringyfied' version. */ hv_store (h, "type", 4, gperl_convert_back_enum (GCONF_TYPE_VALUE_TYPE, v->type), 0); hv_store (h, "value", 5, gconfperl_sv_from_value (v), 0); break; case GCONF_VALUE_PAIR: /* a pair consists of two fundamental types, stored as * car and cdr (damned LISP lovers). We do not supply * accessor methods for GConfValue, so we try to * reflect the storage as much as we can; thus, we * create two keys, 'car' and 'cdr', instead of the * usual 'value' one. The programmer must be warned, * so we leave type as 'pair' (also because car and cdr * may be of two different types, so we need a marker * for this situation). */ { SV * car, * cdr; hv_store (h, "type", 4, gperl_convert_back_enum (GCONF_TYPE_VALUE_TYPE, v->type), 0); car = newSVGConfValue (gconf_value_get_car (v)); cdr = newSVGConfValue (gconf_value_get_cdr (v)); hv_store (h, "car", 3, newSVsv (car), 0); hv_store (h, "cdr", 3, newSVsv (cdr), 0); } break; case GCONF_VALUE_LIST: /* lists are handled like arrayrefs; the type is the * list type, in order to mask the special 'list' type * from the programmer. */ { AV * a; SV * r; GSList * l, * tmp; GConfValueType t = gconf_value_get_list_type (v); a = newAV (); r = newRV_noinc ((SV *) a); /* safe */ l = gconf_value_get_list (v); for (tmp = l; tmp != NULL; tmp = tmp->next) av_push (a, gconfperl_sv_from_value ((GConfValue *) tmp->data)); hv_store (h, "type", 4, gperl_convert_back_enum (GCONF_TYPE_VALUE_TYPE, t), 0); hv_store (h, "value", 5, newSVsv (r), 0); } break; case GCONF_VALUE_INVALID: /* this is used only for error handling */ default: croak ("newSVGConfValue: invalid type found"); break; } stash = gv_stashpv ("Gnome2::GConf::Value", TRUE); sv_bless (sv, stash); return sv; } /* Create a GConfValue from a SV. */ GConfValue * SvGConfValue (SV * data) { HV * h; SV ** s; GConfValue * v; GConfValueType t; int n; if ((!data) || (!SvOK(data)) || (!SvRV(data)) || (SvTYPE(SvRV(data)) != SVt_PVHV)) croak ("SvGConfValue: value must be an hashref"); h = (HV *) SvRV (data); /* retrieve the type */ if (! ((s = hv_fetch (h, "type", 4, 0)) && SvOK (*s))) croak ("SvGConfValue: 'type' key is needed"); /* if it is an integer, just assign it... */ if (looks_like_number (*s)) t = SvIV (*s); /* otherwise, try to convert it from the enum */ if (!gperl_try_convert_enum (GCONF_TYPE_VALUE_TYPE, *s, &n)) croak ("SvGConfValue: 'type' should be either a GConfValueType or an integer"); t = (GConfValueType) n; /* set GConfValue using the right setter method */ switch (t) { case GCONF_VALUE_STRING: case GCONF_VALUE_INT: case GCONF_VALUE_FLOAT: case GCONF_VALUE_BOOL: case GCONF_VALUE_SCHEMA: if (! ((s = hv_fetch (h, "value", 5, 0)) && SvOK (*s))) croak ("SvGConfValue: fundamental types require a value key"); /* the argument is not a reference, so convert it */ if (!SvROK (*s)) { v = gconf_value_new (t); gconfperl_value_from_sv (*s, v); } else if (SvROK (*s) || SvTYPE (SvRV (*s)) == SVt_PVAV) { /* the argument is an array, so fill the list */ AV * av = (AV*) SvRV (*s); GSList * list = NULL; int i; v = gconf_value_new (GCONF_VALUE_LIST); gconf_value_set_list_type (v, t); for (i = av_len (av) ; i >= 0 ; i--) { GConfValue * v = gconf_value_new (t); gconfperl_value_from_sv (*av_fetch (av, i, FALSE), v); list = g_slist_prepend (list, v); } gconf_value_set_list_nocopy (v, list); } else croak ("SvGConfValue: value must be either a " "scalar or an array reference"); break; case GCONF_VALUE_PAIR: { GConfValue * car, * cdr; v = gconf_value_new (GCONF_VALUE_PAIR); /* build up the first value of the pair */ if (! ((s = hv_fetch (h, "car", 3, 0)) && SvOK (*s))) croak ("SvGConfValue: 'pair' type requires a 'car' key"); car = SvGConfValue (*s); gconf_value_set_car_nocopy (v, car); /* and then the second value */ if (! ((s = hv_fetch (h, "cdr", 3, 0)) && SvOK (*s))) croak ("SvGConfValue: 'pair' type requires a 'cdr' key"); cdr = SvGConfValue (*s); gconf_value_set_cdr_nocopy (v, cdr); } break; case GCONF_VALUE_LIST: /* handled above, this should never be passed */ case GCONF_VALUE_INVALID: /* used for error situations */ default: croak ("SvGConfValue: invalid type found."); } return v; } MODULE = Gnome2::GConf::Value PACKAGE = Gnome2::GConf::Value =for object Gnome2::GConf::Value Opaque datatype for generic values =cut =for position SYNOPSIS =head1 SYNOPSIS $client = Gnome2::GConf::Client->get_default; $client->set($config_key, { type => 'string', value => 'Hello, World', }); print "The Meaning of Life." if ($client->get($another_key)->{value} == 42); =cut =for position DESCRIPTION =head1 DESCRIPTION C is a dynamic type similar to C, and represents a value that can be obtained from or stored in the configuration database; it contains the value bound to a key, and its type. In perl, it's an hashref containing these keys: =over =item B The type of the data. Fundamental types are 'string', 'int', 'float' and 'bool'. Lists are handled by passing an arrayref as the payload of the C key: $client->set($key, { type => 'string', value => 'some string' }); $client->set($key, { type => 'float', value => 0.5 }); $client->set($key, { type => 'bool', value => FALSE }); $client->set($key, { type => 'int', value => [0..15] }); Pairs are handled by using the special type 'pair', and passing, in place of the C key, the C and the C keys, each containing an hashref representing a GConfValue: $client->set($key, { type => 'pair', car => { type => 'string', value => 'some string' }, cdr => { type => 'int', value => 42 }, }); This is needed since pairs might have different types; lists, instead, are of the same type. =item B The payload, containing the value of type C. It is used only for fundamental types (scalars or lists). =item B, B Special keys, that must be used only when working with the 'pair' type. =back =cut =for see_also =head1 SEE ALSO L(3pm), L(3pm), L(3pm), L(3pm). =cut ##/* we need to provide a real DESTROY function because GConfValue ## * objects are dynamically allocated ## */ void DESTROY (value) SV * value CODE: gconf_value_free (SvGConfValue (value)); #if GCONF_CHECK_VERSION (2, 13, 1) gint compare (value_a, value_b) GConfValue * value_a GConfValue * value_b CODE: RETVAL = gconf_value_compare (value_a, value_b); OUTPUT: RETVAL #endif /* GCONF_CHECK_VERSION (2, 13, 1) */ gchar_own * to_string (value) GConfValue * value CODE: RETVAL = gconf_value_to_string (value); OUTPUT: RETVAL