gimpconfig-deserialize.c 23.8 KB
Newer Older
1 2
/* LIBGIMP - The GIMP Library
 * Copyright (C) 1995-1997 Spencer Kimball and Peter Mattis
3 4
 *
 * Object properties deserialization routines
5
 * Copyright (C) 2001-2002  Sven Neumann <sven@gimp.org>
6
 *
7 8 9 10
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2 of the License, or (at your option) any later version.
11
 *
12
 * This library is distributed in the hope that it will be useful,
13
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Library General Public License for more details.
16
 *
17 18 19 20
 * You should have received a copy of the GNU Lesser 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.
21 22 23 24 25 26
 */

#include "config.h"

#include <glib-object.h>

27
#include "libgimpbase/gimpbase.h"
28
#include "libgimpcolor/gimpcolor.h"
29
#include "libgimpmath/gimpmath.h"
30

31 32
#include "gimpconfigtypes.h"

33 34
#include "gimpconfigwriter.h"
#include "gimpconfig-iface.h"
35
#include "gimpconfig-deserialize.h"
36
#include "gimpconfig-params.h"
37
#include "gimpconfig-path.h"
Michael Natterer's avatar
Michael Natterer committed
38
#include "gimpscanner.h"
39

40
#include "libgimp/libgimp-intl.h"
41

42

43
/*
44 45 46 47 48
 *  All functions return G_TOKEN_RIGHT_PAREN on success,
 *  the GTokenType they would have expected but didn't get
 *  or G_TOKEN_NONE if they got the expected token but
 *  couldn't parse it.
 */
49

50
static GTokenType  gimp_config_deserialize_value       (GValue     *value,
Sven Neumann's avatar
Sven Neumann committed
51
                                                        GimpConfig *config,
52 53
                                                        GParamSpec *prop_spec,
                                                        GScanner   *scanner);
54 55 56 57 58 59 60 61 62 63 64 65
static GTokenType  gimp_config_deserialize_fundamental (GValue     *value,
                                                        GParamSpec *prop_spec,
                                                        GScanner   *scanner);
static GTokenType  gimp_config_deserialize_enum        (GValue     *value,
                                                        GParamSpec *prop_spec,
                                                        GScanner   *scanner);
static GTokenType  gimp_config_deserialize_memsize     (GValue     *value,
                                                        GParamSpec *prop_spec,
                                                        GScanner   *scanner);
static GTokenType  gimp_config_deserialize_path        (GValue     *value,
                                                        GParamSpec *prop_spec,
                                                        GScanner   *scanner);
Michael Natterer's avatar
Michael Natterer committed
66
static GTokenType  gimp_config_deserialize_rgb         (GValue     *value,
67 68
                                                        GParamSpec *prop_spec,
                                                        GScanner   *scanner);
69 70 71
static GTokenType  gimp_config_deserialize_matrix2     (GValue     *value,
                                                        GParamSpec *prop_spec,
                                                        GScanner   *scanner);
72
static GTokenType  gimp_config_deserialize_object      (GValue     *value,
Sven Neumann's avatar
Sven Neumann committed
73
                                                        GimpConfig *config,
74 75 76
                                                        GParamSpec *prop_spec,
                                                        GScanner   *scanner,
                                                        gint        nest_level);
77
static GTokenType  gimp_config_deserialize_value_array (GValue     *value,
Sven Neumann's avatar
Sven Neumann committed
78
                                                        GimpConfig *config,
79 80
                                                        GParamSpec *prop_spec,
                                                        GScanner   *scanner);
81 82 83
static GTokenType  gimp_config_deserialize_any         (GValue     *value,
                                                        GParamSpec *prop_spec,
                                                        GScanner   *scanner);
84
static GTokenType  gimp_config_skip_unknown_property   (GScanner   *scanner);
85

86
static inline gboolean  scanner_string_utf8_valid (GScanner    *scanner,
87
                                                   const gchar *token_name);
88

89 90 91 92 93 94 95 96 97 98 99 100 101
static inline gboolean
scanner_string_utf8_valid (GScanner    *scanner,
                           const gchar *token_name)
{
  if (g_utf8_validate (scanner->value.v_string, -1, NULL))
    return TRUE;

  g_scanner_error (scanner,
                   _("value for token %s is not a valid UTF-8 string"),
                   token_name);

  return FALSE;
}
102

103 104
/**
 * gimp_config_deserialize_properties:
Sven Neumann's avatar
Sven Neumann committed
105
 * @config: a #GimpConfig.
106
 * @scanner: a #GScanner.
107
 * @nest_level:
108
 *
Sven Neumann's avatar
Sven Neumann committed
109
 * This function uses the @scanner to configure the properties of @config.
110
 *
111 112 113
 * Return value: %TRUE on success, %FALSE otherwise.
 *
 * Since: GIMP 2.4
114
 **/
115
gboolean
116 117
gimp_config_deserialize_properties (GimpConfig *config,
                                    GScanner   *scanner,
Sven Neumann's avatar
Sven Neumann committed
118
                                    gint        nest_level)
119
{
120 121 122 123 124 125
  GObjectClass  *klass;
  GParamSpec   **property_specs;
  guint          n_property_specs;
  guint          i;
  guint          scope_id;
  guint          old_scope_id;
126 127
  GTokenType     token;
  GTokenType     next;
128

129
  g_return_val_if_fail (GIMP_IS_CONFIG (config), FALSE);
130

131
  klass = G_OBJECT_GET_CLASS (config);
132
  property_specs = g_object_class_list_properties (klass, &n_property_specs);
133

134
  if (! property_specs)
135
    return TRUE;
136

137
  scope_id = g_type_qname (G_TYPE_FROM_INSTANCE (config));
138 139 140 141 142 143
  old_scope_id = g_scanner_set_scope (scanner, scope_id);

  for (i = 0; i < n_property_specs; i++)
    {
      GParamSpec *prop_spec = property_specs[i];

144
      if (prop_spec->flags & GIMP_CONFIG_PARAM_SERIALIZE)
145
        {
146
          g_scanner_scope_add_symbol (scanner, scope_id,
147 148 149 150
                                      prop_spec->name, prop_spec);
        }
    }

151 152
  g_free (property_specs);

153
  g_object_freeze_notify (G_OBJECT (config));
154

155
  token = G_TOKEN_LEFT_PAREN;
156 157

  while (TRUE)
158
    {
159 160
      next = g_scanner_peek_next_token (scanner);

161 162 163 164 165 166
      if (G_UNLIKELY (next != token &&
                      ! (token == G_TOKEN_SYMBOL &&
                         next  == G_TOKEN_IDENTIFIER)))
        {
          break;
        }
167 168

      token = g_scanner_get_next_token (scanner);
169

170 171 172 173 174
      switch (token)
        {
        case G_TOKEN_LEFT_PAREN:
          token = G_TOKEN_SYMBOL;
          break;
175

176 177 178 179
        case G_TOKEN_IDENTIFIER:
          token = gimp_config_skip_unknown_property (scanner);
          break;

180
        case G_TOKEN_SYMBOL:
181
          token = gimp_config_deserialize_property (config,
182
                                                    scanner, nest_level);
183 184 185 186 187 188 189 190 191 192 193
          break;

        case G_TOKEN_RIGHT_PAREN:
          token = G_TOKEN_LEFT_PAREN;
          break;

        default: /* do nothing */
          break;
        }
    }

194 195
  g_scanner_set_scope (scanner, old_scope_id);

196
  g_object_thaw_notify (G_OBJECT (config));
197

198 199 200
  if (token == G_TOKEN_NONE)
    return FALSE;

201
  return gimp_config_deserialize_return (scanner, token, nest_level);
202 203
}

204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
/**
 * gimp_config_deserialize_property:
 * @config: a #GimpConfig.
 * @scanner: a #GScanner.
 * @nest_level:
 *
 * This function deserializes a single property of @config. You
 * shouldn't need to call this function directly. If possible, use
 * gimp_config_deserialize_properties() instead.
 *
 * Return value: %G_TOKEN_RIGHT_PAREN on success, otherwise the
 * expected #GTokenType or %G_TOKEN_NONE if the expected token was
 * found but couldn't be parsed.
 *
 * Since: GIMP 2.4
 **/
220
GTokenType
221
gimp_config_deserialize_property (GimpConfig *config,
222 223
                                  GScanner   *scanner,
                                  gint        nest_level)
224
{
225
  GimpConfigInterface *config_iface = NULL;
226
  GimpConfigInterface *parent_iface = NULL;
227 228 229
  GParamSpec          *prop_spec;
  GTokenType           token = G_TOKEN_RIGHT_PAREN;
  GValue               value = { 0, };
230 231 232
  guint                old_scope_id;

  old_scope_id = g_scanner_set_scope (scanner, 0);
233

234
  prop_spec = G_PARAM_SPEC (scanner->value.v_symbol);
235

236 237
  g_value_init (&value, prop_spec->value_type);

238
  if (G_TYPE_IS_OBJECT (prop_spec->owner_type))
239
    {
240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
      GTypeClass *owner_class = g_type_class_peek (prop_spec->owner_type);

      config_iface = g_type_interface_peek (owner_class, GIMP_TYPE_CONFIG);

      /*  We must call deserialize_property() *only* if the *exact* class
       *  which implements it is param_spec->owner_type's class.
       *
       *  Therefore, we ask param_spec->owner_type's immediate parent class
       *  for it's GimpConfigInterface and check if we get a different
       *  pointer.
       *
       *  (if the pointers are the same, param_spec->owner_type's
       *   GimpConfigInterface is inherited from one of it's parent classes
       *   and thus not able to handle param_spec->owner_type's properties).
       */
      if (config_iface)
        {
          GTypeClass *owner_parent_class;
258

259
          owner_parent_class = g_type_class_peek_parent (owner_class);
260

261 262 263
          parent_iface = g_type_interface_peek (owner_parent_class,
                                                GIMP_TYPE_CONFIG);
        }
264 265
    }

266
  if (config_iface                       &&
267
      config_iface != parent_iface       && /* see comment above */
268 269 270 271 272 273 274
      config_iface->deserialize_property &&
      config_iface->deserialize_property (config,
                                          prop_spec->param_id,
                                          &value,
                                          prop_spec,
                                          scanner,
                                          &token))
275 276 277 278 279
    {
      /* nop */
    }
  else
    {
280 281
      if (G_VALUE_HOLDS_OBJECT (&value))
        token = gimp_config_deserialize_object (&value,
282
                                                config, prop_spec,
283 284 285
                                                scanner, nest_level);
      else
        token = gimp_config_deserialize_value (&value,
286
                                               config, prop_spec, scanner);
287
    }
288

289 290 291
  if (token == G_TOKEN_RIGHT_PAREN &&
      g_scanner_peek_next_token (scanner) == token)
    {
292
      if (! (G_VALUE_HOLDS_OBJECT (&value) &&
293
             (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE)))
294
        g_object_set_property (G_OBJECT (config), prop_spec->name, &value);
295
    }
296
#ifdef CONFIG_DEBUG
297
  else
298
    {
299 300
      g_warning ("%s: couldn't deserialize property %s::%s of type %s",
                 G_STRFUNC,
301
                 g_type_name (G_TYPE_FROM_INSTANCE (config)),
302
                 prop_spec->name,
303 304
                 g_type_name (prop_spec->value_type));
    }
305
#endif
306

307
  g_value_unset (&value);
308

309 310
  g_scanner_set_scope (scanner, old_scope_id);

311
  return token;
312 313
}

314 315
static GTokenType
gimp_config_deserialize_value (GValue     *value,
Sven Neumann's avatar
Sven Neumann committed
316
                               GimpConfig *config,
317 318 319 320 321 322 323 324 325 326 327 328 329 330 331
                               GParamSpec *prop_spec,
                               GScanner   *scanner)
{
  if (G_TYPE_FUNDAMENTAL (prop_spec->value_type) == G_TYPE_ENUM)
    {
      return gimp_config_deserialize_enum (value, prop_spec, scanner);
    }
  else if (G_TYPE_IS_FUNDAMENTAL (prop_spec->value_type))
    {
      return gimp_config_deserialize_fundamental (value, prop_spec, scanner);
    }
  else if (prop_spec->value_type == GIMP_TYPE_MEMSIZE)
    {
      return gimp_config_deserialize_memsize (value, prop_spec, scanner);
    }
332
  else if (prop_spec->value_type == GIMP_TYPE_CONFIG_PATH)
333
    {
334
      return  gimp_config_deserialize_path (value, prop_spec, scanner);
335
    }
Michael Natterer's avatar
Michael Natterer committed
336
  else if (prop_spec->value_type == GIMP_TYPE_RGB)
337
    {
Michael Natterer's avatar
Michael Natterer committed
338
      return gimp_config_deserialize_rgb (value, prop_spec, scanner);
339
    }
340 341 342 343
  else if (prop_spec->value_type == GIMP_TYPE_MATRIX2)
    {
      return gimp_config_deserialize_matrix2 (value, prop_spec, scanner);
    }
344 345 346
  else if (prop_spec->value_type == G_TYPE_VALUE_ARRAY)
    {
      return gimp_config_deserialize_value_array (value,
Sven Neumann's avatar
Sven Neumann committed
347
                                                  config, prop_spec, scanner);
348 349 350 351 352 353 354 355
    }

  /*  This fallback will only work for value_types that
   *  can be transformed from a string value.
   */
  return gimp_config_deserialize_any (value, prop_spec, scanner);
}

356
static GTokenType
357 358
gimp_config_deserialize_fundamental (GValue     *value,
                                     GParamSpec *prop_spec,
359
                                     GScanner   *scanner)
360
{
361
  GTokenType token;
Sven Neumann's avatar
Sven Neumann committed
362 363
  GType      value_type;
  gboolean   negate = FALSE;
364

Sven Neumann's avatar
Sven Neumann committed
365 366 367
  value_type = G_TYPE_FUNDAMENTAL (prop_spec->value_type);

  switch (value_type)
368 369
    {
    case G_TYPE_STRING:
370
      token = G_TOKEN_STRING;
371
      break;
372

373
    case G_TYPE_BOOLEAN:
374
      token = G_TOKEN_IDENTIFIER;
375
      break;
376

377 378
    case G_TYPE_INT:
    case G_TYPE_LONG:
379
    case G_TYPE_INT64:
Sven Neumann's avatar
Sven Neumann committed
380 381 382 383 384 385 386
      if (g_scanner_peek_next_token (scanner) == '-')
        {
          negate = TRUE;
          g_scanner_get_next_token (scanner);
        }
      /*  fallthrough  */
    case G_TYPE_UINT:
387
    case G_TYPE_ULONG:
388
    case G_TYPE_UINT64:
389
      token = G_TOKEN_INT;
390
      break;
391

392 393
    case G_TYPE_FLOAT:
    case G_TYPE_DOUBLE:
394 395 396 397 398
      if (g_scanner_peek_next_token (scanner) == '-')
        {
          negate = TRUE;
          g_scanner_get_next_token (scanner);
        }
399
      token = G_TOKEN_FLOAT;
400
      break;
401

402
    default:
403
      token = G_TOKEN_NONE;
404
      g_assert_not_reached ();
405
      break;
406 407
    }

408
  if (g_scanner_peek_next_token (scanner) != token)
Sven Neumann's avatar
Sven Neumann committed
409 410 411
    {
      return token;
    }
412 413 414

  g_scanner_get_next_token (scanner);

Sven Neumann's avatar
Sven Neumann committed
415
  switch (value_type)
416
    {
417
    case G_TYPE_STRING:
418
      if (scanner_string_utf8_valid (scanner, prop_spec->name))
419
        g_value_set_static_string (value, scanner->value.v_string);
420 421
      else
        return G_TOKEN_NONE;
422
      break;
423

424 425 426 427 428 429 430
    case G_TYPE_BOOLEAN:
      if (! g_ascii_strcasecmp (scanner->value.v_identifier, "yes") ||
          ! g_ascii_strcasecmp (scanner->value.v_identifier, "true"))
        g_value_set_boolean (value, TRUE);
      else if (! g_ascii_strcasecmp (scanner->value.v_identifier, "no") ||
               ! g_ascii_strcasecmp (scanner->value.v_identifier, "false"))
        g_value_set_boolean (value, FALSE);
431
      else
432
        {
433 434
          g_scanner_error
            (scanner,
435
             /* please don't translate 'yes' and 'no' */
436
             _("expected 'yes' or 'no' for boolean token %s, got '%s'"),
437
             prop_spec->name, scanner->value.v_identifier);
438
          return G_TOKEN_NONE;
439 440 441 442
        }
      break;

    case G_TYPE_INT:
Sven Neumann's avatar
Sven Neumann committed
443
      g_value_set_int (value, (negate ?
444 445
                               - scanner->value.v_int64 :
                               scanner->value.v_int64));
446 447
      break;
    case G_TYPE_UINT:
448
      g_value_set_uint (value, scanner->value.v_int64);
449
      break;
450

451
    case G_TYPE_LONG:
Sven Neumann's avatar
Sven Neumann committed
452
      g_value_set_long (value, (negate ?
453 454
                                - scanner->value.v_int64 :
                                scanner->value.v_int64));
455 456
      break;
    case G_TYPE_ULONG:
457 458 459 460 461 462 463
      g_value_set_ulong (value, scanner->value.v_int64);
      break;

    case G_TYPE_INT64:
      g_value_set_int64 (value, (negate ?
                                 - scanner->value.v_int64 :
                                 scanner->value.v_int64));
464
      break;
465 466 467 468
    case G_TYPE_UINT64:
      g_value_set_uint64 (value, scanner->value.v_int64);
      break;

469
    case G_TYPE_FLOAT:
470 471
      g_value_set_float (value, negate ?
                         - scanner->value.v_float : scanner->value.v_float);
472 473
      break;
    case G_TYPE_DOUBLE:
474 475
      g_value_set_double (value, negate ?
                          - scanner->value.v_float: scanner->value.v_float);
476
      break;
477

478
    default:
479 480
      g_assert_not_reached ();
      break;
481
    }
482

483
  return G_TOKEN_RIGHT_PAREN;
484 485
}

486
static GTokenType
487 488
gimp_config_deserialize_enum (GValue     *value,
                              GParamSpec *prop_spec,
489
                              GScanner   *scanner)
490 491 492 493 494
{
  GEnumClass *enum_class;
  GEnumValue *enum_value;

  enum_class = g_type_class_peek (G_VALUE_TYPE (value));
495 496

  switch (g_scanner_peek_next_token (scanner))
497
    {
498 499 500
    case G_TOKEN_IDENTIFIER:
      g_scanner_get_next_token (scanner);

501
      enum_value = g_enum_get_value_by_nick (enum_class,
Sven Neumann's avatar
Sven Neumann committed
502
                                             scanner->value.v_identifier);
503
      if (!enum_value)
Sven Neumann's avatar
Sven Neumann committed
504 505
        enum_value = g_enum_get_value_by_name (enum_class,
                                               scanner->value.v_identifier);
506 507

      if (!enum_value)
Sven Neumann's avatar
Sven Neumann committed
508 509 510 511 512 513
        {
          g_scanner_error (scanner,
                           _("invalid value '%s' for token %s"),
                           scanner->value.v_identifier, prop_spec->name);
          return G_TOKEN_NONE;
        }
514
      break;
515

516 517 518
    case G_TOKEN_INT:
      g_scanner_get_next_token (scanner);

519 520
      enum_value = g_enum_get_value (enum_class,
                                     (gint) scanner->value.v_int64);
521 522

      if (!enum_value)
Sven Neumann's avatar
Sven Neumann committed
523 524 525 526 527 528
        {
          g_scanner_error (scanner,
                           _("invalid value '%ld' for token %s"),
                           (glong) scanner->value.v_int64, prop_spec->name);
          return G_TOKEN_NONE;
        }
529
      break;
530

531 532
    default:
      return G_TOKEN_IDENTIFIER;
533
    }
534

535 536
  g_value_set_enum (value, enum_value->value);

537
  return G_TOKEN_RIGHT_PAREN;
538 539
}

540 541 542 543 544
static GTokenType
gimp_config_deserialize_memsize (GValue     *value,
                                 GParamSpec *prop_spec,
                                 GScanner   *scanner)
{
545 546 547
  gchar   *orig_cset_first = scanner->config->cset_identifier_first;
  gchar   *orig_cset_nth   = scanner->config->cset_identifier_nth;
  guint64  memsize;
548 549 550

  scanner->config->cset_identifier_first = G_CSET_DIGITS;
  scanner->config->cset_identifier_nth   = G_CSET_DIGITS "gGmMkKbB";
551

552 553
  if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
    return G_TOKEN_IDENTIFIER;
554

555 556 557 558
  g_scanner_get_next_token (scanner);

  scanner->config->cset_identifier_first = orig_cset_first;
  scanner->config->cset_identifier_nth   = orig_cset_nth;
559

560
  if (! gimp_memsize_deserialize (scanner->value.v_identifier, &memsize))
561
    return G_TOKEN_NONE;
562 563 564 565

  g_value_set_uint64 (value, memsize);

  return G_TOKEN_RIGHT_PAREN;
566 567 568 569 570 571 572
}

static GTokenType
gimp_config_deserialize_path (GValue     *value,
                              GParamSpec *prop_spec,
                              GScanner   *scanner)
{
573
  GError *error = NULL;
574

575 576 577 578 579
  if (g_scanner_peek_next_token (scanner) != G_TOKEN_STRING)
    return G_TOKEN_STRING;

  g_scanner_get_next_token (scanner);

580 581 582
  if (!scanner_string_utf8_valid (scanner, prop_spec->name))
    return G_TOKEN_NONE;

583 584 585
  if (scanner->value.v_string)
    {
      /*  Check if the string can be expanded
586
       *  and converted to the filesystem encoding.
587 588 589
       */
      gchar *expand = gimp_config_path_expand (scanner->value.v_string,
                                               TRUE, &error);
590

591 592 593
      if (!expand)
        {
          g_scanner_error (scanner,
594
                           _("while parsing token '%s': %s"),
595 596
                           prop_spec->name, error->message);
          g_error_free (error);
597

598 599
          return G_TOKEN_NONE;
        }
600

601
      g_free (expand);
602

603 604
      g_value_set_static_string (value, scanner->value.v_string);
    }
605 606 607 608

  return G_TOKEN_RIGHT_PAREN;
}

609
static GTokenType
Michael Natterer's avatar
Michael Natterer committed
610 611 612
gimp_config_deserialize_rgb (GValue     *value,
                             GParamSpec *prop_spec,
                             GScanner   *scanner)
613
{
Michael Natterer's avatar
Michael Natterer committed
614
  GimpRGB rgb;
615

Michael Natterer's avatar
Michael Natterer committed
616
  if (! gimp_scanner_parse_color (scanner, &rgb))
617
    return G_TOKEN_NONE;
618

Michael Natterer's avatar
Michael Natterer committed
619
  g_value_set_boxed (value, &rgb);
620

621
  return G_TOKEN_RIGHT_PAREN;
622 623
}

624 625 626 627 628 629 630 631 632 633 634 635 636 637 638
static GTokenType
gimp_config_deserialize_matrix2 (GValue     *value,
                                 GParamSpec *prop_spec,
                                 GScanner   *scanner)
{
  GimpMatrix2 matrix;

  if (! gimp_scanner_parse_matrix2 (scanner, &matrix))
    return G_TOKEN_NONE;

  g_value_set_boxed (value, &matrix);

  return G_TOKEN_RIGHT_PAREN;
}

639 640
static GTokenType
gimp_config_deserialize_object (GValue     *value,
641
                                GimpConfig *config,
642 643 644 645
                                GParamSpec *prop_spec,
                                GScanner   *scanner,
                                gint        nest_level)
{
646 647
  GimpConfigInterface *config_iface;
  GimpConfig          *prop_object;
648

649
  g_object_get_property (G_OBJECT (config), prop_spec->name, value);
650 651 652 653

  prop_object = g_value_get_object (value);

  if (! prop_object)
654
    {
655
      /*  if the object property is not GIMP_CONFIG_PARAM_AGGREGATE, read
656 657
       *  the type of the object and create it
       */
658
      if (! (prop_spec->flags & GIMP_CONFIG_PARAM_AGGREGATE))
659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680
        {
          gchar *type_name;
          GType  type;

          if (! gimp_scanner_parse_string (scanner, &type_name))
            return G_TOKEN_STRING;

          type = g_type_from_name (type_name);
          g_free (type_name);

          if (! g_type_is_a (type, prop_spec->value_type))
            return G_TOKEN_STRING;

          prop_object = g_object_new (type, NULL);

          g_value_take_object (value, prop_object);
        }
      else
        {
          return G_TOKEN_RIGHT_PAREN;
        }
    }
681

682
  config_iface = GIMP_CONFIG_GET_INTERFACE (prop_object);
683

684
  if (! config_iface)
685
    return gimp_config_deserialize_any (value, prop_spec, scanner);
686

687
  if (! config_iface->deserialize (prop_object, scanner, nest_level + 1, NULL))
688 689 690 691 692
    return G_TOKEN_NONE;

  return G_TOKEN_RIGHT_PAREN;
}

693 694
static GTokenType
gimp_config_deserialize_value_array (GValue     *value,
695
                                     GimpConfig *config,
696 697 698 699 700 701 702 703 704 705 706 707
                                     GParamSpec *prop_spec,
                                     GScanner   *scanner)
{
  GParamSpecValueArray *array_spec;
  GValueArray          *array;
  GValue                array_value = { 0, };
  gint                  n_values;
  GTokenType            token;
  gint                  i;

  array_spec = G_PARAM_SPEC_VALUE_ARRAY (prop_spec);

708
  if (! gimp_scanner_parse_int (scanner, &n_values))
709 710 711 712 713 714 715 716 717
    return G_TOKEN_INT;

  array = g_value_array_new (n_values);

  for (i = 0; i < n_values; i++)
    {
      g_value_init (&array_value, array_spec->element_spec->value_type);

      token = gimp_config_deserialize_value (&array_value,
718
                                             config,
719 720 721 722 723 724 725 726 727 728 729 730
                                             array_spec->element_spec,
                                             scanner);

      if (token == G_TOKEN_RIGHT_PAREN)
        g_value_array_append (array, &array_value);

      g_value_unset (&array_value);

      if (token != G_TOKEN_RIGHT_PAREN)
        return token;
    }

731
  g_value_take_boxed (value, array);
732

733 734 735
  return G_TOKEN_RIGHT_PAREN;
}

736
static GTokenType
737 738
gimp_config_deserialize_any (GValue     *value,
                             GParamSpec *prop_spec,
739
                             GScanner   *scanner)
740
{
741
  GValue src = { 0, };
742 743 744 745

  if (!g_value_type_transformable (G_TYPE_STRING, prop_spec->value_type))
    {
      g_warning ("%s: %s can not be transformed from a string",
746
                 G_STRFUNC, g_type_name (prop_spec->value_type));
747
      return G_TOKEN_NONE;
748
    }
749

750 751
  if (g_scanner_peek_next_token (scanner) != G_TOKEN_IDENTIFIER)
    return G_TOKEN_IDENTIFIER;
752 753 754 755

  g_scanner_get_next_token (scanner);

  g_value_init (&src, G_TYPE_STRING);
756
  g_value_set_static_string (&src, scanner->value.v_identifier);
757
  g_value_transform (&src, value);
758
  g_value_unset (&src);
759

760
  return G_TOKEN_RIGHT_PAREN;
761
}
762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795

static GTokenType
gimp_config_skip_unknown_property (GScanner *scanner)
{
  gint open_paren = 0;

  while (TRUE)
    {
      GTokenType token = g_scanner_peek_next_token (scanner);

      switch (token)
        {
        case G_TOKEN_LEFT_PAREN:
          open_paren++;
          g_scanner_get_next_token (scanner);
          break;

        case G_TOKEN_RIGHT_PAREN:
          if (open_paren == 0)
            return token;

          open_paren--;
          g_scanner_get_next_token (scanner);
          break;

        case G_TOKEN_EOF:
          return token;

        default:
          g_scanner_get_next_token (scanner);
          break;
        }
    }
}