1 /* Fo
2 * fo-property.h: FoProperty superclass of all property classes
3 *
4 * Copyright (C) 2001 Sun Microsystems
5 * Copyright (C) 2007-2008 Menteith Consulting Ltd
6 *
7 * See COPYING for the status of this software.
8 */
9
10 #include "fo-utils.h"
11 #include "fo-object.h"
12 #include "datatype/fo-all-datatype.h"
13 #include "fo-context.h"
14 #include "fo-property-private.h"
15 #include "fo-property-font-size.h"
16 /*#include <expression-parser.h>*/
17
18 /**
19 * SECTION:fo-property
20 * @short_description: Abstract FO property type
21 *
22 * Every property is a subtype of #FoProperty.
23 */
24
25 const gchar *fo_property_error_messages [] = {
26 N_("Property error."),
27 N_("Property expression evaluation failed."),
28 N_("Attempt to convert to enumeration value when property has no values defined."),
29 N_("Values of '%s' property cannot be negative: %s"),
30 N_("Value of property '%s' should not be zero-length.")
31 };
32
33 enum {
34 PROP_0,
35 PROP_VALUE,
36 PROP_IS_INHERITED,
37 PROP_IS_SHORTHAND
38 };
39
40 static void fo_property_base_init (FoPropertyClass *klass);
41 static void fo_property_base_finalize (FoPropertyClass *klass);
42 static void fo_property_class_init (FoPropertyClass *klass);
43 static void fo_property_set_property (GObject *object,
44 guint prop_id,
45 const GValue *value,
46 GParamSpec *pspec);
47 static void fo_property_get_property (GObject *object,
48 guint prop_id,
49 GValue *value,
50 GParamSpec *pspec);
51 static void fo_property_finalize (GObject *object);
52
53 static FoDatatype* fo_property_get_value_default (FoProperty *property);
54 static void fo_property_set_value_default (FoProperty *property,
55 FoDatatype *new_value);
56 static FoDatatype* fo_property_resolve_enum_default (const gchar *token,
57 FoContext *context,
58 GError **error);
59 static FoDatatype* fo_property_resolve_percent_default (gdouble percentage,
60 const FoDatatype *font_size,
61 const FoFo *fo_node,
62 const FoContext *context,
63 GError **err);
64 static FoProperty* fo_property_new_from_expr_default (FoPropertyClass *property_class,
65 const gchar *expr,
66 FoContext *context,
67 FoProperty *current_font_size,
68 FoFo *fo_node,
69 GError **error);
70 gchar* fo_property_sprintf (FoObject *object);
71 static void fo_property_debug_dump (FoObject *object,
72 gint depth);
73 static gpointer parent_class;
74
75 GQuark
fo_property_error_quark(void)76 fo_property_error_quark (void)
77 {
78 static GQuark quark = 0;
79 if (quark == 0)
80 quark = g_quark_from_static_string ("Property error");
81 return quark;
82 }
83
84 /**
85 * fo_property_get_type:
86 *
87 * Register the #FoProperty object type if not already registered and
88 * return its #GType value.
89 *
90 * Return value: #GType value of the #FoProperty object type.
91 **/
92 GType
fo_property_get_type(void)93 fo_property_get_type (void)
94 {
95 static GType object_type = 0;
96
97 if (!object_type)
98 {
99 static const GTypeInfo object_info =
100 {
101 sizeof (FoPropertyClass),
102 (GBaseInitFunc) fo_property_base_init,
103 (GBaseFinalizeFunc) fo_property_base_finalize,
104 (GClassInitFunc) fo_property_class_init,
105 NULL, /* class_finalize */
106 NULL, /* class_data */
107 sizeof (FoProperty),
108 0, /* n_preallocs */
109 NULL, /* instance_init */
110 NULL /* value_table */
111 };
112
113 object_type = g_type_register_static (FO_TYPE_OBJECT,
114 "FoProperty",
115 &object_info,
116 G_TYPE_FLAG_ABSTRACT);
117 }
118
119 return object_type;
120 }
121
122 /**
123 * fo_property_base_init:
124 * @klass: #FoPropertyClass object to initialise.
125 *
126 * Implements #GBaseInitFunc for #FoPropertyClass.
127 **/
128 void
fo_property_base_init(FoPropertyClass * klass)129 fo_property_base_init (FoPropertyClass *klass)
130 {
131 FoObjectClass *fo_object_class = FO_OBJECT_CLASS (klass);
132
133 klass->is_inherited = FALSE;
134 klass->is_shorthand = FALSE;
135 klass->expr_env_list = fo_expr_env_list_new ();
136 klass->new_from_expr = fo_property_new_from_expr_default;
137 klass->expr_eval = fo_expr_eval;
138 klass->set_value = fo_property_set_value_default;
139 klass->get_value = fo_property_get_value_default;
140 klass->resolve_percent = fo_property_resolve_percent_default;
141
142 fo_object_class->print_sprintf = fo_property_sprintf;
143 fo_object_class->debug_dump = fo_property_debug_dump;
144 }
145
146 /**
147 * fo_property_base_finalize:
148 * @klass: #FoPropertyClass object to finalise.
149 *
150 * Implements #GBaseFinalizeFunc for #FoPropertyClass.
151 **/
152 void
fo_property_base_finalize(FoPropertyClass * klass)153 fo_property_base_finalize (FoPropertyClass *klass)
154 {
155 fo_expr_env_list_free (klass->expr_env_list);
156 }
157
158 /**
159 * fo_property_class_init:
160 * @klass: #FoPropertyClass object to initialise.
161 *
162 * Implements #GClassInitFunc for #FoPropertyClass.
163 **/
164 void
fo_property_class_init(FoPropertyClass * klass)165 fo_property_class_init (FoPropertyClass *klass)
166 {
167 GObjectClass *object_class = G_OBJECT_CLASS (klass);
168
169 parent_class = g_type_class_peek_parent (klass);
170
171 object_class->finalize = fo_property_finalize;
172
173 object_class->set_property = fo_property_set_property;
174 object_class->get_property = fo_property_get_property;
175
176 g_object_class_install_property (object_class,
177 PROP_VALUE,
178 g_param_spec_object ("value",
179 _("property value"),
180 _("Property value"),
181 FO_TYPE_DATATYPE,
182 G_PARAM_READWRITE));
183 g_object_class_install_property
184 (object_class,
185 PROP_IS_INHERITED,
186 g_param_spec_boolean ("is-inherited",
187 _("Is inherited?"),
188 _("Is this an inherited property?"),
189 TRUE,
190 G_PARAM_READABLE));
191 g_object_class_install_property
192 (object_class,
193 PROP_IS_SHORTHAND,
194 g_param_spec_boolean ("is-shorthand",
195 _("Is shorthand?"),
196 _("Is this a shorthand property?"),
197 TRUE,
198 G_PARAM_READABLE));
199
200 klass->resolve_enum = fo_property_resolve_enum_default;
201 }
202
203 /**
204 * fo_property_finalize:
205 * @object: #FoProperty object to finalize.
206 *
207 * Implements #GObjectFinalizeFunc for #FoProperty.
208 **/
209 void
fo_property_finalize(GObject * object)210 fo_property_finalize (GObject *object)
211 {
212 FoProperty *property;
213
214 property = FO_PROPERTY (object);
215
216 if (property->value != NULL)
217 {
218 g_object_unref (property->value);
219 property->value = NULL;
220 }
221
222 G_OBJECT_CLASS (parent_class)->finalize (object);
223 }
224
225
226 /**
227 * fo_property_set_property:
228 * @object: #GObject whose property will be set.
229 * @prop_id: Property ID assigned when property registered.
230 * @value: New value for property.
231 * @pspec: Parameter specification for this property type.
232 *
233 * Implements #GObjectSetPropertyFunc for #FoProperty.
234 **/
235 void
fo_property_set_property(GObject * object,guint prop_id,const GValue * value,GParamSpec * pspec)236 fo_property_set_property (GObject *object,
237 guint prop_id,
238 const GValue *value,
239 GParamSpec *pspec)
240 {
241 FoProperty *property;
242
243 property = FO_PROPERTY (object);
244
245 switch (prop_id)
246 {
247 case PROP_VALUE:
248 fo_property_set_value (property, g_value_get_object (value));
249 break;
250 default:
251 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
252 break;
253 }
254 }
255
256 /**
257 * fo_property_get_property:
258 * @object: #GObject whose property will be retreived.
259 * @prop_id: Property ID assigned when property registered.
260 * @value: GValue to set with property value.
261 * @pspec: Parameter specification for this property type.
262 *
263 * Implements #GObjectGetPropertyFunc for #FoProperty.
264 **/
265 void
fo_property_get_property(GObject * object,guint prop_id,GValue * value,GParamSpec * pspec)266 fo_property_get_property (GObject *object,
267 guint prop_id,
268 GValue *value,
269 GParamSpec *pspec)
270 {
271 FoProperty *property;
272
273 property = FO_PROPERTY (object);
274
275 switch (prop_id)
276 {
277 case PROP_VALUE:
278 g_value_set_instance (value,
279 fo_property_get_value (property));
280 break;
281 case PROP_IS_INHERITED:
282 g_value_set_boolean (value,
283 fo_property_is_inherited (property));
284 break;
285 case PROP_IS_SHORTHAND:
286 g_value_set_boolean (value,
287 fo_property_is_shorthand (property));
288 break;
289 default:
290 G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
291 break;
292 }
293 }
294
295 /**
296 * fo_property_get_value:
297 * @property: #FoProperty object whose value is to be retrieved.
298 *
299 * Returns the current value property value of @property.
300 *
301 * Return value: The current 'value' property value.
302 **/
303 FoDatatype*
fo_property_get_value(FoProperty * property)304 fo_property_get_value (FoProperty *property)
305 {
306 g_return_val_if_fail (property != NULL, NULL);
307 g_return_val_if_fail (FO_IS_PROPERTY (property), NULL);
308
309 return FO_PROPERTY_GET_CLASS (property)->get_value (property);
310 }
311
312 /**
313 * fo_property_set_value:
314 * @property: #FoProperty object whose value is to be set.
315 * @new_value: New value for the property.
316 *
317 * Sets the 'value' property of @property.
318 **/
319 void
fo_property_set_value(FoProperty * property,FoDatatype * new_value)320 fo_property_set_value (FoProperty *property,
321 FoDatatype *new_value)
322 {
323 g_return_if_fail (property != NULL);
324 g_return_if_fail (FO_IS_PROPERTY (property));
325
326 FO_PROPERTY_GET_CLASS (property)->set_value (property, new_value);
327 }
328
329 /**
330 * fo_property_get_value_default:
331 * @property: FoProperty object whose value is to be retrieved
332 *
333 * Returns the current #value property value of @property
334 *
335 * Return value: The current #value property value
336 **/
337 FoDatatype*
fo_property_get_value_default(FoProperty * property)338 fo_property_get_value_default (FoProperty *property)
339 {
340 g_return_val_if_fail (property != NULL, NULL);
341 g_return_val_if_fail (FO_IS_PROPERTY (property), NULL);
342
343 return property->value;
344 }
345
346 /**
347 * fo_property_set_value_default:
348 * @property: FoProperty object whose value is to be set
349 * @new_value: New value for the property
350 *
351 * Sets the #value property of @property
352 **/
353 void
fo_property_set_value_default(FoProperty * property,FoDatatype * new_value)354 fo_property_set_value_default (FoProperty *property,
355 FoDatatype *new_value)
356 {
357 g_return_if_fail (property != NULL);
358 g_return_if_fail (FO_IS_PROPERTY (property));
359 g_return_if_fail (new_value == NULL || FO_IS_DATATYPE (new_value));
360
361 if (new_value != NULL)
362 g_object_ref (G_OBJECT (new_value));
363 if (property->value != NULL)
364 g_object_unref (G_OBJECT (property->value));
365 property->value = new_value;
366 /*g_object_notify(G_OBJECT(property), "value");*/
367 }
368
369 /**
370 * fo_property_resolve_enum_default:
371 * @token:
372 * @context:
373 * @error:
374 *
375 *
376 *
377 * Return value:
378 **/
379 FoDatatype*
fo_property_resolve_enum_default(const gchar * token G_GNUC_UNUSED,FoContext * context G_GNUC_UNUSED,GError ** error)380 fo_property_resolve_enum_default (const gchar *token G_GNUC_UNUSED,
381 FoContext *context G_GNUC_UNUSED,
382 GError **error)
383 {
384 g_warning ("This property doesn't have any enumerated values.\n");
385
386 g_set_error (error,
387 FO_PROPERTY_ERROR,
388 FO_PROPERTY_ERROR_NO_ENUMERATION,
389 "%s",
390 fo_property_error_messages[FO_PROPERTY_ERROR_NO_ENUMERATION]);
391 return NULL;
392 }
393
394 /**
395 * fo_property_resolve_percent_default:
396 * @percentage: Percentage value to resolve.
397 * @font_size: Font size to use if a percentage of font-size.
398 * @fo_node: Current #FoFo.
399 * @context: #FoContext of parent #FoFo.
400 * @err: Indicates whether an error occurs.
401 *
402 * Resolves @percentage as a percentage of a known value which,
403 * depending on the property, could be the current font-size value or
404 * some property value of the current #FoFo or its first #FoArea.
405 *
406 * Return value: #FoDatatype representing resolved value.
407 **/
408 FoDatatype*
fo_property_resolve_percent_default(gdouble percentage,const FoDatatype * font_size G_GNUC_UNUSED,const FoFo * fo_node G_GNUC_UNUSED,const FoContext * context G_GNUC_UNUSED,GError ** err G_GNUC_UNUSED)409 fo_property_resolve_percent_default (gdouble percentage,
410 const FoDatatype *font_size G_GNUC_UNUSED,
411 const FoFo *fo_node G_GNUC_UNUSED,
412 const FoContext *context G_GNUC_UNUSED,
413 GError **err G_GNUC_UNUSED)
414 {
415 FoDatatype *percentage_datatype;
416
417 percentage_datatype = fo_percentage_new ();
418 fo_percentage_set_value (percentage_datatype,
419 percentage);
420
421 return percentage_datatype;
422 }
423
424 /**
425 * fo_property_new_from_string:
426 * @property_class: #FoPropertyClass or subclass of property.
427 * @expr: Expression to be evaluated as a string.
428 * @context: #FoContext for inherited values.
429 * @current_font_size: Current font size for resolving 'em' values.
430 * @fo_node: The current #FoFo node.
431 * @error: #GError for indicating any error that occurs.
432 *
433 * Evaluate @expr and create a new instance of @property_class with
434 * value of @expr.
435 *
436 * Other parameters (except @error) provide context for evaluating
437 * @expr.
438 *
439 * Return value: A new instance of @property_class, or NULL if error.
440 **/
441 FoProperty*
fo_property_new_from_string(FoPropertyClass * property_class,const gchar * expr,FoContext * context,FoProperty * current_font_size,FoFo * fo_node G_GNUC_UNUSED,GError ** error)442 fo_property_new_from_string (FoPropertyClass *property_class,
443 const gchar *expr,
444 FoContext *context,
445 FoProperty *current_font_size,
446 FoFo *fo_node G_GNUC_UNUSED,
447 GError **error)
448 {
449 FoProperty *property;
450 FoDatatype *datatype;
451 GError *tmp_error = NULL;
452 const gchar *property_name = NULL;
453
454 g_return_val_if_fail (FO_IS_PROPERTY_CLASS (property_class), NULL);
455 g_return_val_if_fail (expr != NULL, NULL);
456 g_return_val_if_fail (context != NULL, NULL);
457 g_return_val_if_fail (FO_IS_CONTEXT (context), NULL);
458 g_return_val_if_fail
459 (current_font_size == NULL || FO_IS_PROPERTY_FONT_SIZE (current_font_size),
460 NULL);
461 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
462
463 property_name = G_OBJECT_CLASS_NAME (property_class);
464
465 datatype = fo_string_new_with_value (expr);
466
467 datatype = property_class->validate (datatype, context, &tmp_error);
468
469 if (tmp_error != NULL)
470 {
471 g_propagate_error (error, tmp_error);
472 return NULL;
473 }
474
475 property =
476 (FoProperty *) g_object_new (G_OBJECT_CLASS_TYPE (property_class),
477 "value",
478 datatype,
479 NULL);
480
481 return property;
482 }
483
484 /**
485 * fo_property_new_from_expr_default:
486 * @property_class: #FoPropertyClass or subclass of property.
487 * @expr: Expression to be evaluated.
488 * @context: #FoContext for inherited values.
489 * @current_font_size: Current font size for resolving 'em' values.
490 * @fo_node: The current #FoFo node.
491 * @error: #GError for indicating any error that occurs.
492 *
493 * Evaluate @expr and create a new instance of @property_class with
494 * value of @expr.
495 *
496 * Other parameters (except @error) provide context for evaluating
497 * @expr.
498 *
499 * Return value: A new instance of @property_class, or NULL if error.
500 **/
501 FoProperty*
fo_property_new_from_expr_default(FoPropertyClass * property_class,const gchar * expr,FoContext * context,FoProperty * current_font_size,FoFo * fo_node,GError ** error)502 fo_property_new_from_expr_default (FoPropertyClass *property_class,
503 const gchar *expr,
504 FoContext *context,
505 FoProperty *current_font_size,
506 FoFo *fo_node,
507 GError **error)
508 {
509 GError *tmp_error = NULL;
510
511 g_return_val_if_fail (FO_IS_PROPERTY_CLASS (property_class), NULL);
512 g_return_val_if_fail (expr != NULL, NULL);
513 g_return_val_if_fail (context != NULL, NULL);
514 g_return_val_if_fail (FO_IS_CONTEXT (context), NULL);
515 g_return_val_if_fail
516 (current_font_size == NULL || FO_IS_PROPERTY_FONT_SIZE (current_font_size),
517 NULL);
518 g_return_val_if_fail (error == NULL || *error == NULL, NULL);
519
520 const gchar *property_name = G_OBJECT_CLASS_NAME (property_class);
521
522 FoDatatype *datatype =
523 property_class->expr_eval (expr,
524 property_name,
525 property_class->resolve_enum,
526 property_class->resolve_percent,
527 current_font_size,
528 fo_node,
529 context,
530 property_class->expr_env_list,
531 &tmp_error);
532
533 if (tmp_error != NULL)
534 {
535 g_propagate_error (error, tmp_error);
536 return NULL;
537 }
538
539 datatype = property_class->validate (datatype, context, &tmp_error);
540
541 if (tmp_error != NULL)
542 {
543 g_propagate_error (error, tmp_error);
544 return NULL;
545 }
546
547 g_object_ref_sink (datatype);
548
549 FoProperty *property =
550 (FoProperty *) g_object_new (G_OBJECT_CLASS_TYPE (property_class),
551 "value",
552 datatype,
553 NULL);
554
555 return property;
556 }
557
558 /**
559 * fo_property_new_from_expr:
560 * @property_class: Class of property for which to evaluate expression.
561 * @expr: Expression to evaluate.
562 * @context: Current #FoContext.
563 * @current_font_size: Current "font-size" value
564 * @fo_node: Current FO
565 * @error: Indication of any error that occurred
566 *
567 * Evaluates @expr as a value of an instance of @property_class.
568 * The other parameters provide the context for evaluating @expr.
569 *
570 * Returns: New instance of @property_class, or %NULL if an
571 * error ocurred.
572 **/
573 FoProperty*
fo_property_new_from_expr(FoPropertyClass * property_class,const gchar * expr,FoContext * context,FoProperty * current_font_size,FoFo * fo_node,GError ** error)574 fo_property_new_from_expr (FoPropertyClass *property_class,
575 const gchar *expr,
576 FoContext *context,
577 FoProperty *current_font_size,
578 FoFo *fo_node,
579 GError **error)
580 {
581 g_return_val_if_fail (FO_IS_PROPERTY_CLASS (property_class), NULL);
582
583 if (*expr == '\0')
584 {
585 const gchar *property_name = G_OBJECT_CLASS_NAME (property_class);
586
587 g_set_error (error,
588 FO_PROPERTY_ERROR,
589 FO_PROPERTY_ERROR_ZERO_LENGTH,
590 fo_property_error_messages[FO_PROPERTY_ERROR_ZERO_LENGTH],
591 property_name);
592
593 return NULL;
594 }
595
596 return property_class->new_from_expr (property_class,
597 expr,
598 context,
599 current_font_size,
600 fo_node,
601 error);
602 }
603 /**
604 * fo_property_class_is_inherited:
605 * @property_class: #FoPropertyClass or a subclass of #FoPropertyClass.
606 *
607 * Indicates whether the XSL property represented by @property_class
608 * is defined in the XSL 1.0 Recommendation as an inherited property.
609 *
610 * Return value: TRUE if @property_class is inherited, FALSE if not.
611 **/
612 gboolean
fo_property_class_is_inherited(FoPropertyClass * property_class)613 fo_property_class_is_inherited (FoPropertyClass *property_class)
614 {
615 g_return_val_if_fail (FO_IS_PROPERTY_CLASS (property_class), FALSE);
616
617 return (FO_PROPERTY_CLASS (property_class)->is_inherited);
618 }
619
620 /**
621 * fo_property_is_inherited:
622 * @property: #FoProperty or subclass of #FoProperty
623 *
624 * Indicates whether the XSL property represented by @property is
625 * defined in the XSL 1.0 Recommendation as an inherited property.
626 *
627 * Return value: TRUE if @property is inherited, FALSE otherwise
628 **/
629 gboolean
fo_property_is_inherited(FoProperty * property)630 fo_property_is_inherited (FoProperty *property)
631 {
632 g_return_val_if_fail (FO_IS_PROPERTY (property), FALSE);
633
634 return fo_property_class_is_inherited (FO_PROPERTY_GET_CLASS (property));
635 }
636
637 /**
638 * fo_property_class_is_shorthand:
639 * @property_class: #FoPropertyClass or a subclass of #FoPropertyClass.
640 *
641 * Indicates whether the XSL property represented by @property_class
642 * is defined in the XSL 1.0 Recommendation as a shorthand property.
643 *
644 * Return value: TRUE if @property_class is a shorthand, FALSE if not.
645 **/
646 gboolean
fo_property_class_is_shorthand(FoPropertyClass * property_class)647 fo_property_class_is_shorthand (FoPropertyClass *property_class)
648 {
649 g_return_val_if_fail (FO_IS_PROPERTY_CLASS (property_class), FALSE);
650
651 return (FO_PROPERTY_CLASS (property_class)->is_shorthand);
652 }
653
654 /**
655 * fo_property_is_shorthand:
656 * @property: #FoProperty or a subclass of #FoProperty.
657 *
658 * Indicates whether the XSL property represented by @property is
659 * defined in the XSL 1.0 Recommendation as a shorthand property.
660 *
661 * Return value: TRUE if @property is a shorthand, FALSE otherwise
662 **/
663 gboolean
fo_property_is_shorthand(FoProperty * property)664 fo_property_is_shorthand (FoProperty *property)
665 {
666 g_return_val_if_fail (FO_IS_PROPERTY (property), FALSE);
667
668 return fo_property_class_is_shorthand (FO_PROPERTY_GET_CLASS (property));
669 }
670
671 /**
672 * fo_property_sprintf:
673 * @object: Object to be named
674 *
675 * Returns a string representing the property and its current value.
676 * The returned value should be freed when no longer needed.
677 *
678 * Return value: A newly allocated string
679 **/
680 gchar*
fo_property_sprintf(FoObject * object)681 fo_property_sprintf (FoObject *object)
682 {
683 g_return_val_if_fail (object != NULL, NULL);
684 g_return_val_if_fail (FO_IS_PROPERTY (object), NULL);
685
686 return
687 g_strdup_printf ("%s: %s",
688 g_type_name (G_TYPE_FROM_INSTANCE (object)),
689 fo_object_sprintf (FO_PROPERTY (object)->value));
690 }
691
692 /**
693 * fo_property_debug_dump:
694 * @object:
695 * @depth:
696 *
697 *
698 **/
699 void
fo_property_debug_dump(FoObject * object,gint depth)700 fo_property_debug_dump (FoObject *object, gint depth)
701 {
702 gchar *indent = g_strnfill (depth * 2, ' ');
703 gchar* object_sprintf;
704
705 g_return_if_fail (object != NULL);
706 g_return_if_fail (FO_IS_PROPERTY (object));
707
708 object_sprintf = fo_object_debug_sprintf (object);
709
710 g_log (G_LOG_DOMAIN,
711 G_LOG_LEVEL_DEBUG,
712 "%s%s",
713 indent,
714 object_sprintf);
715
716 g_free (object_sprintf);
717 g_free (indent);
718
719 fo_object_debug_dump (fo_property_get_value (FO_PROPERTY (object)),
720 depth + 2);
721 }
722