Set Nautilus-Actions as being the actual official product name
[nautilus-actions.git] / src / io-xml / naxml-reader.c
blob8064a535f9635fe6d849472f2588c09ac50c172c
1 /*
2 * Nautilus-Actions
3 * A Nautilus extension which offers configurable context menu actions.
5 * Copyright (C) 2005 The GNOME Foundation
6 * Copyright (C) 2006, 2007, 2008 Frederic Ruaudel and others (see AUTHORS)
7 * Copyright (C) 2009, 2010, 2011 Pierre Wieser and others (see AUTHORS)
9 * This Program is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU General Public License as
11 * published by the Free Software Foundation; either version 2 of
12 * the License, or (at your option) any later version.
14 * This Program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this Library; see the file COPYING. If not,
21 * write to the Free Software Foundation, Inc., 59 Temple Place,
22 * Suite 330, Boston, MA 02111-1307, USA.
24 * Authors:
25 * Frederic Ruaudel <grumz@grumz.net>
26 * Rodrigo Moya <rodrigo@gnome-db.org>
27 * Pierre Wieser <pwieser@trychlos.org>
28 * ... and many others (see AUTHORS)
31 #ifdef HAVE_CONFIG_H
32 #include <config.h>
33 #endif
35 #include <glib/gi18n.h>
36 #include <libxml/tree.h>
37 #include <string.h>
39 #include <api/na-core-utils.h>
40 #include <api/na-gconf-utils.h>
41 #include <api/na-data-types.h>
42 #include <api/na-ifactory-provider.h>
43 #include <api/na-object-api.h>
45 #include <io-gconf/nagp-keys.h>
47 #include "naxml-keys.h"
48 #include "naxml-reader.h"
50 /* private class data
52 struct NAXMLReaderClassPrivate {
53 void *empty; /* so that gcc -pedantic is happy */
56 /* the association between a document root node key and the functions
58 typedef struct {
59 gchar *root_key;
60 gchar *list_key;
61 gchar *element_key;
62 gchar *key_entry;
63 guint key_length;
64 guint ( *fn_root_parms ) ( NAXMLReader *, xmlNode * );
65 guint ( *fn_list_parms ) ( NAXMLReader *, xmlNode * );
66 guint ( *fn_element_parms ) ( NAXMLReader *, xmlNode * );
67 guint ( *fn_element_content )( NAXMLReader *, xmlNode * );
68 gchar * ( *fn_get_value ) ( NAXMLReader *, xmlNode *, const NADataDef *def );
70 RootNodeStr;
72 /* private instance data
73 * main naxml_reader_import_from_uri() function is called once for each file
74 * to import. We thus have one NAXMLReader object per import operation.
76 struct NAXMLReaderPrivate {
77 gboolean dispose_has_run;
79 /* data provided by the caller
81 NAIImporter *importer;
82 NAIImporterImportFromUriParms *parms;
84 /* data dynamically set during the import operation
86 gboolean type_found;
87 GList *nodes;
88 GList *dealt;
89 RootNodeStr *root_node_str;
90 gchar *item_id;
92 /* following values are reset and reused while iterating on each
93 * element nodes of the imported item (cf. reset_node_data())
95 gboolean node_ok;
98 extern NAXMLKeyStr naxml_schema_key_schema_str[];
99 extern NAXMLKeyStr naxml_dump_key_entry_str[];
101 static GObjectClass *st_parent_class = NULL;
103 static GType register_type( void );
104 static void class_init( NAXMLReaderClass *klass );
105 static void instance_init( GTypeInstance *instance, gpointer klass );
106 static void instance_dispose( GObject *object );
107 static void instance_finalize( GObject *object );
109 static NAXMLReader *reader_new( void );
111 static guint schema_parse_schema_content( NAXMLReader *reader, xmlNode *node );
112 static void schema_check_for_id( NAXMLReader *reader, xmlNode *iter );
113 static void schema_check_for_type( NAXMLReader *reader, xmlNode *iter );
114 static gchar *schema_read_value( NAXMLReader *reader, xmlNode *node, const NADataDef *def );
116 static guint dump_parse_list_parms( NAXMLReader *reader, xmlNode *node );
117 static guint dump_parse_entry_content( NAXMLReader *reader, xmlNode *node );
118 static void dump_check_for_type( NAXMLReader *reader, xmlNode *key_node );
119 static gchar *dump_read_value( NAXMLReader *reader, xmlNode *node, const NADataDef *def );
121 static RootNodeStr st_root_node_str[] = {
123 { NAXML_KEY_SCHEMA_ROOT,
124 NAXML_KEY_SCHEMA_LIST,
125 NAXML_KEY_SCHEMA_NODE,
126 NAXML_KEY_SCHEMA_NODE_APPLYTO,
128 NULL,
129 NULL,
130 NULL,
131 schema_parse_schema_content,
132 schema_read_value },
134 { NAXML_KEY_DUMP_ROOT,
135 NAXML_KEY_DUMP_LIST,
136 NAXML_KEY_DUMP_NODE,
137 NAXML_KEY_DUMP_NODE_KEY,
139 NULL,
140 dump_parse_list_parms,
141 NULL,
142 dump_parse_entry_content,
143 dump_read_value },
145 { NULL }
148 #define ERR_ITEM_ID_NOT_FOUND _( "Item ID not found." )
149 #define ERR_MENU_UNWAITED _( "Unwaited key path %s while importing a menu." )
150 #define ERR_NODE_ALREADY_FOUND _( "Element %s at line %d already found, ignored." )
151 #define ERR_NODE_INVALID_ID _( "Invalid item ID: waited for %s, found %s at line %d." )
152 #define ERR_NODE_UNKNOWN _( "Unknown element %s found at line %d while waiting for %s." )
153 /* i18n: do not translate keywords 'Action' nor 'Menu' */
154 #define ERR_NODE_UNKNOWN_TYPE _( "Unknown type %s found at line %d, while waiting for Action or Menu." )
155 #define ERR_ROOT_UNKNOWN _( "Invalid XML root element %s found at line %d while waiting for %s." )
156 #define ERR_XMLDOC_UNABLE_TOPARSE _( "Unable to parse XML file: %s." )
157 #define WARN_UNDEALT_NODE _( "Node %s at line %d has not been dealt with." )
159 static void read_start_profile_attach_profile( NAXMLReader *reader, NAObjectProfile *profile );
160 static gboolean read_data_is_path_adhoc_for_object( NAXMLReader *reader, const NAIFactoryObject *object, xmlChar *text );
161 static NADataBoxed *read_data_boxed_from_node( NAXMLReader *reader, xmlChar *text, xmlNode *parent, const NADataDef *def );
162 static void read_done_item_set_localized_icon( NAXMLReader *reader, NAObjectItem *item );
163 static void read_done_action_read_profiles( NAXMLReader *reader, NAObjectAction *action );
164 static gchar *read_done_action_get_next_profile_id( NAXMLReader *reader );
165 static void read_done_action_load_profile( NAXMLReader *reader, const gchar *profile_id );
166 static void read_done_profile_set_localized_label( NAXMLReader *reader, NAObjectProfile *profile );
168 static guint reader_parse_xmldoc( NAXMLReader *reader );
169 static guint iter_on_root_children( NAXMLReader *reader, xmlNode *root );
170 static guint iter_on_list_children( NAXMLReader *reader, xmlNode *first );
172 static gchar *build_key_node_list( NAXMLKeyStr *strlist );
173 static gchar *build_root_node_list( void );
174 static gchar *get_value_from_child_node( xmlNode *node, const gchar *child );
175 static gchar *get_value_from_child_child_node( xmlNode *node, const gchar *first, const gchar *second );
176 static gboolean is_profile_path( NAXMLReader *reader, xmlChar *text );
177 static guint manage_import_mode( NAXMLReader *reader );
178 static void publish_undealt_nodes( NAXMLReader *reader );
179 static void reset_node_data( NAXMLReader *reader );
180 static xmlNode *search_for_child_node( xmlNode *node, const gchar *key );
181 static int strxcmp( const xmlChar *a, const char *b );
183 GType
184 naxml_reader_get_type( void )
186 static GType object_type = 0;
188 if( !object_type ){
189 object_type = register_type();
192 return( object_type );
195 static GType
196 register_type( void )
198 static const gchar *thisfn = "naxml_reader_register_type";
199 GType type;
201 static GTypeInfo info = {
202 sizeof( NAXMLReaderClass ),
203 NULL,
204 NULL,
205 ( GClassInitFunc ) class_init,
206 NULL,
207 NULL,
208 sizeof( NAXMLReader ),
210 ( GInstanceInitFunc ) instance_init
213 g_debug( "%s", thisfn );
215 type = g_type_register_static( G_TYPE_OBJECT, "NAXMLReader", &info, 0 );
217 return( type );
220 static void
221 class_init( NAXMLReaderClass *klass )
223 static const gchar *thisfn = "naxml_reader_class_init";
224 GObjectClass *object_class;
226 g_debug( "%s: klass=%p", thisfn, ( void * ) klass );
228 st_parent_class = g_type_class_peek_parent( klass );
230 object_class = G_OBJECT_CLASS( klass );
231 object_class->dispose = instance_dispose;
232 object_class->finalize = instance_finalize;
234 klass->private = g_new0( NAXMLReaderClassPrivate, 1 );
237 static void
238 instance_init( GTypeInstance *instance, gpointer klass )
240 static const gchar *thisfn = "naxml_reader_instance_init";
241 NAXMLReader *self;
243 g_debug( "%s: instance=%p, klass=%p", thisfn, ( void * ) instance, ( void * ) klass );
244 g_return_if_fail( NAXML_IS_READER( instance ));
245 self = NAXML_READER( instance );
247 self->private = g_new0( NAXMLReaderPrivate, 1 );
249 self->private->dispose_has_run = FALSE;
250 self->private->importer = NULL;
251 self->private->parms = NULL;
252 self->private->type_found = FALSE;
253 self->private->nodes = NULL;
254 self->private->dealt = NULL;
255 self->private->root_node_str = NULL;
258 static void
259 instance_dispose( GObject *object )
261 static const gchar *thisfn = "naxml_reader_instance_dispose";
262 NAXMLReader *self;
264 g_debug( "%s: object=%p", thisfn, ( void * ) object );
265 g_return_if_fail( NAXML_IS_READER( object ));
266 self = NAXML_READER( object );
268 if( !self->private->dispose_has_run ){
270 self->private->dispose_has_run = TRUE;
272 g_list_free( self->private->nodes );
273 g_list_free( self->private->dealt );
275 /* chain up to the parent class */
276 if( G_OBJECT_CLASS( st_parent_class )->dispose ){
277 G_OBJECT_CLASS( st_parent_class )->dispose( object );
282 static void
283 instance_finalize( GObject *object )
285 static const gchar *thisfn = "naxml_reader_instance_finalize";
286 NAXMLReader *self;
288 g_debug( "%s: object=%p", thisfn, ( void * ) object );
289 g_return_if_fail( NAXML_IS_READER( object ));
290 self = NAXML_READER( object );
292 g_free( self->private->item_id );
294 reset_node_data( self );
296 g_free( self->private );
298 /* chain call to parent class */
299 if( G_OBJECT_CLASS( st_parent_class )->finalize ){
300 G_OBJECT_CLASS( st_parent_class )->finalize( object );
304 static NAXMLReader *
305 reader_new( void )
307 return( g_object_new( NAXML_READER_TYPE, NULL ));
311 * naxml_reader_import_uri:
312 * @instance: the #NAIImporter provider.
313 * @parms: a #NAIImporterUriParms structure.
315 * Imports an item.
317 * Returns: the import operation code.
319 guint
320 naxml_reader_import_from_uri( const NAIImporter *instance, NAIImporterImportFromUriParms *parms )
322 static const gchar *thisfn = "naxml_reader_import_from_uri";
323 NAXMLReader *reader;
324 guint code;
326 g_debug( "%s: instance=%p, parms=%p", thisfn, ( void * ) instance, ( void * ) parms );
328 g_return_val_if_fail( NA_IS_IIMPORTER( instance ), IMPORTER_CODE_PROGRAM_ERROR );
330 reader = reader_new();
331 reader->private->importer = ( NAIImporter * ) instance;
332 reader->private->parms = parms;
334 parms->exist = FALSE;
335 parms->import_mode = IMPORTER_MODE_NO_IMPORT;
336 parms->imported = NULL;
338 code = reader_parse_xmldoc( reader );
340 if( code == IMPORTER_CODE_OK ){
341 g_assert( NA_IS_OBJECT_ITEM( reader->private->parms->imported ));
342 code = manage_import_mode( reader );
345 if( code != IMPORTER_CODE_OK ){
346 if( reader->private->parms->imported ){
347 g_object_unref( reader->private->parms->imported );
348 reader->private->parms->imported = NULL;
352 if( code == IMPORTER_CODE_OK ){
353 publish_undealt_nodes( reader );
356 g_object_unref( reader );
358 return( code );
362 * check that the file is a valid XML document
363 * and that the root node can be identified as a schema or a dump
365 static guint
366 reader_parse_xmldoc( NAXMLReader *reader )
368 RootNodeStr *istr;
369 gboolean found;
370 guint code;
372 xmlDoc *doc = xmlParseFile( reader->private->parms->uri );
374 if( !doc ){
375 xmlErrorPtr error = xmlGetLastError();
376 na_core_utils_slist_add_message( &reader->private->parms->messages,
377 ERR_XMLDOC_UNABLE_TOPARSE, error->message );
378 xmlResetError( error );
379 code = IMPORTER_CODE_NOT_WILLING_TO;
381 } else {
382 xmlNode *root_node = xmlDocGetRootElement( doc );
384 istr = st_root_node_str;
385 found = FALSE;
387 while( istr->root_key && !found ){
388 if( !strxcmp( root_node->name, istr->root_key )){
389 found = TRUE;
390 reader->private->root_node_str = istr;
391 code = iter_on_root_children( reader, root_node );
393 istr++;
396 if( !found ){
397 gchar *node_list = build_root_node_list();
398 na_core_utils_slist_add_message( &reader->private->parms->messages,
399 ERR_ROOT_UNKNOWN,
400 ( const char * ) root_node->name, root_node->line, node_list );
401 g_free( node_list );
402 code = IMPORTER_CODE_NOT_WILLING_TO;
405 xmlFreeDoc (doc);
408 xmlCleanupParser();
409 return( code );
413 * parse a XML tree
414 * - must have one child on the named 'first_child' key (others are warned)
415 * - then iter on child nodes of this previous first named which must ne 'next_child'
417 static guint
418 iter_on_root_children( NAXMLReader *reader, xmlNode *root )
420 static const gchar *thisfn = "naxml_reader_iter_on_root_children";
421 xmlNodePtr iter;
422 gboolean found;
423 guint code;
425 g_debug( "%s: reader=%p, root=%p", thisfn, ( void * ) reader, ( void * ) root );
427 code = IMPORTER_CODE_OK;
429 /* deal with properties attached to the root node
431 if( reader->private->root_node_str->fn_root_parms ){
432 code = ( *reader->private->root_node_str->fn_root_parms )( reader, root );
435 /* iter through the first level of children (list)
436 * we must have only one occurrence of this first 'list' child
438 found = FALSE;
439 for( iter = root->children ; iter && code == IMPORTER_CODE_OK ; iter = iter->next ){
441 if( iter->type != XML_ELEMENT_NODE ){
442 continue;
445 if( strxcmp( iter->name, reader->private->root_node_str->list_key )){
446 na_core_utils_slist_add_message( &reader->private->parms->messages,
447 ERR_NODE_UNKNOWN,
448 ( const char * ) iter->name, iter->line, reader->private->root_node_str->list_key );
449 continue;
452 if( found ){
453 na_core_utils_slist_add_message( &reader->private->parms->messages, ERR_NODE_ALREADY_FOUND, ( const char * ) iter->name, iter->line );
454 continue;
457 found = TRUE;
458 code = iter_on_list_children( reader, iter );
461 return( code );
465 * iter on 'schema/entry' element nodes
466 * each node should correspond to an elementary data of the imported item
467 * other nodes are warned (and ignored)
469 * we have to iterate a first time through all nodes to be sure to find
470 * a potential 'type' indication - this is needed in order to allocate an
471 * action or a menu - if not found at the end of this first pass, we default
472 * to allocate an action
474 * this first pass is also used to check nodes
476 * - for each node, check that
477 * > 'schema/entry' childs are in the list of known schema/entry child nodes
478 * > 'schema/entry' childs appear only once per node
479 * -> this requires a per-node 'found' flag which is reset for each node
480 * > schema has an 'applyto' child node
481 * -> only checkable at the end of the schema
483 * - check that each data, identified by the 'applyto' value, appears only once
484 * applyto node -> elementary data + id item + (optionally) id profile
485 * elementary data -> group (action, menu, profile)
486 * -> this requires a 'found' flag for each group+data reset at item level
487 * as the item may not be allocated yet, we cannot check that data
488 * is actually relevant with the to-be-imported item
490 * each schema 'applyto' node let us identify a data and its value
492 static guint
493 iter_on_list_children( NAXMLReader *reader, xmlNode *list )
495 static const gchar *thisfn = "naxml_reader_iter_on_list_children";
496 guint code;
497 xmlNode *iter;
499 g_debug( "%s: reader=%p, list=%p", thisfn, ( void * ) reader, ( void * ) list );
501 code = IMPORTER_CODE_OK;
503 /* deal with properties attached to the list node
505 if( reader->private->root_node_str->fn_list_parms ){
506 code = ( *reader->private->root_node_str->fn_list_parms )( reader, list );
509 /* each occurrence should correspond to an elementary data
510 * we run first to determine the type, and allocate the object
511 * we then rely on NAIFactoryProvider to actually read the data
513 for( iter = list->children ; iter && code == IMPORTER_CODE_OK ; iter = iter->next ){
515 if( iter->type != XML_ELEMENT_NODE ){
516 continue;
519 if( strxcmp( iter->name, reader->private->root_node_str->element_key )){
520 na_core_utils_slist_add_message( &reader->private->parms->messages,
521 ERR_NODE_UNKNOWN,
522 ( const char * ) iter->name, iter->line, reader->private->root_node_str->element_key );
523 continue;
526 reset_node_data( reader );
528 if( reader->private->root_node_str->fn_element_parms ){
529 code = ( *reader->private->root_node_str->fn_element_parms )( reader, iter );
530 if( code != IMPORTER_CODE_OK ){
531 continue;
535 if( reader->private->root_node_str->fn_element_content ){
536 code = ( *reader->private->root_node_str->fn_element_content )( reader, iter );
537 if( code != IMPORTER_CODE_OK ){
538 continue;
542 if( reader->private->node_ok ){
543 reader->private->nodes = g_list_prepend( reader->private->nodes, iter );
547 /* check that we have a not empty id
549 if( code == IMPORTER_CODE_OK ){
550 if( !reader->private->item_id || !strlen( reader->private->item_id )){
551 na_core_utils_slist_add_message( &reader->private->parms->messages, ERR_ITEM_ID_NOT_FOUND );
552 code = IMPORTER_CODE_NO_ITEM_ID;
556 /* if type not found, then suppose that we have an action
558 if( code == IMPORTER_CODE_OK ){
560 if( !reader->private->type_found ){
561 reader->private->parms->imported = NA_OBJECT_ITEM( na_object_action_new());
565 /* now load the data
567 if( code == IMPORTER_CODE_OK ){
569 na_object_set_id( reader->private->parms->imported, reader->private->item_id );
571 na_ifactory_provider_read_item(
572 NA_IFACTORY_PROVIDER( reader->private->importer ),
573 reader,
574 NA_IFACTORY_OBJECT( reader->private->parms->imported ),
575 &reader->private->parms->messages );
578 return( code );
581 void
582 naxml_reader_read_start( const NAIFactoryProvider *provider, void *reader_data, const NAIFactoryObject *object, GSList **messages )
584 static const gchar *thisfn = "naxml_reader_read_start";
586 g_return_if_fail( NA_IS_IFACTORY_PROVIDER( provider ));
587 g_return_if_fail( NA_IS_IFACTORY_OBJECT( object ));
589 g_debug( "%s: provider=%p, reader_data=%p, object=%p (%s), messages=%p",
590 thisfn,
591 ( void * ) provider,
592 ( void * ) reader_data,
593 ( void * ) object, G_OBJECT_TYPE_NAME( object ),
594 ( void * ) messages );
596 if( NA_IS_OBJECT_PROFILE( object )){
597 read_start_profile_attach_profile( NAXML_READER( reader_data ), NA_OBJECT_PROFILE( object ));
601 static void
602 read_start_profile_attach_profile( NAXMLReader *reader, NAObjectProfile *profile )
604 na_object_attach_profile( reader->private->parms->imported, profile );
608 * this callback function is called by NAIFactoryObject once for each
609 * serializable data for the object
611 * Note that some nodes may be readen twice because of multiple definition
612 * of the same data (e.g. icon which exists in localized and unlocalized
613 * versions). So do not remove dealt-with nodes here
615 NADataBoxed *
616 naxml_reader_read_data( const NAIFactoryProvider *provider, void *reader_data, const NAIFactoryObject *object, const NADataDef *def, GSList **messages )
618 static const gchar *thisfn = "naxml_reader_read_data";
619 xmlNode *parent_node;
620 GList *ielt;
622 g_return_val_if_fail( NA_IS_IFACTORY_PROVIDER( provider ), NULL );
623 g_return_val_if_fail( NA_IS_IFACTORY_OBJECT( object ), NULL );
625 g_debug( "%s: reader_data=%p, object=%p (%s), data=%s",
626 thisfn, ( void * ) reader_data, ( void * ) object, G_OBJECT_TYPE_NAME( object ), def->name );
628 if( !def->gconf_entry || !strlen( def->gconf_entry )){
629 g_warning( "%s: GConf entry is not set for NADataDef %s", thisfn, def->name );
630 return( NULL );
633 NADataBoxed *boxed = NULL;
634 NAXMLReader *reader = NAXML_READER( reader_data );
636 /*g_debug( "naxml_reader_read_data: nodes=%p (count=%d)",
637 ( void * ) reader->private->nodes, g_list_length( reader->private->nodes ));*/
638 for( ielt = reader->private->nodes ; ielt && !boxed ; ielt = ielt->next ){
640 parent_node = ( xmlNode * ) ielt->data;
641 xmlNode *entry_node = search_for_child_node( parent_node, reader->private->root_node_str->key_entry );
643 if( !entry_node ){
644 g_warning( "%s: no '%s' child in node at line %u", thisfn, reader->private->root_node_str->key_entry, parent_node->line );
646 } else {
647 xmlChar *path = xmlNodeGetContent( entry_node );
648 /*g_debug( "%s: found %s=%s", thisfn, def->gconf_entry, ( const gchar * ) path );*/
650 if( read_data_is_path_adhoc_for_object( reader, object, path )){
651 boxed = read_data_boxed_from_node( reader, path, parent_node, def );
654 xmlFree( path );
658 if( boxed ){
659 reader->private->dealt = g_list_prepend( reader->private->dealt, parent_node );
662 return( boxed );
665 static gboolean
666 read_data_is_path_adhoc_for_object( NAXMLReader *reader, const NAIFactoryObject *object, xmlChar *text )
668 gboolean adhoc;
669 GSList *path_slist;
670 guint path_length;
671 gchar *node_profile_id;
672 gchar *factory_profile_id;
674 adhoc = TRUE;
675 path_slist = na_core_utils_slist_from_split(( const gchar * ) text, "/" );
676 path_length = g_slist_length( path_slist );
678 if( NA_IS_OBJECT_ITEM( object )){
679 if( path_length != reader->private->root_node_str->key_length ){
680 adhoc = FALSE;
683 } else if( !is_profile_path( reader, text )){
684 adhoc = FALSE;
685 /*g_debug( "%s not adhoc as not profile path", ( const gchar * ) text );*/
687 } else {
688 gchar *key_dirname = g_path_get_dirname(( const gchar * ) text );
689 node_profile_id = g_path_get_basename( key_dirname );
690 g_free( key_dirname );
692 factory_profile_id = na_object_get_id( object );
694 if( strcmp( node_profile_id, factory_profile_id ) != 0 ){
695 adhoc = FALSE;
696 /*g_debug( "%s not adhoc (%s) as not searched profile %s",
697 ( const gchar * ) text, node_profile_id, factory_profile_id );*/
700 g_free( factory_profile_id );
701 g_free( node_profile_id );
704 na_core_utils_slist_free( path_slist );
706 return( adhoc );
709 static NADataBoxed *
710 read_data_boxed_from_node( NAXMLReader *reader, xmlChar *path, xmlNode *parent, const NADataDef *def )
712 NADataBoxed *boxed;
713 gchar *entry;
714 gchar *value;
716 boxed = NULL;
717 entry = g_path_get_basename(( const gchar * ) path );
719 /*g_debug( "read_data_boxed_from_node: node_entry=%s def_gconf=%s",
720 entry, def->gconf_entry );*/
722 /* read the value
724 if( !strcmp( entry, def->gconf_entry )){
726 if( reader->private->root_node_str->fn_get_value ){
727 value = ( *reader->private->root_node_str->fn_get_value )( reader, parent, def );
728 boxed = na_data_boxed_new( def );
729 na_data_boxed_set_from_string( boxed, value );
730 g_free( value );
734 g_free( entry );
736 return( boxed );
740 * all serializable data of the object has been readen
742 void
743 naxml_reader_read_done( const NAIFactoryProvider *provider, void *reader_data, const NAIFactoryObject *object, GSList **messages )
745 static const gchar *thisfn = "naxml_reader_read_done";
747 g_return_if_fail( NA_IS_IFACTORY_PROVIDER( provider ));
748 g_return_if_fail( NA_IS_IFACTORY_OBJECT( object ));
750 g_debug( "%s: provider=%p, reader_data=%p, object=%p (%s), messages=%p",
751 thisfn,
752 ( void * ) provider,
753 ( void * ) reader_data,
754 ( void * ) object, G_OBJECT_TYPE_NAME( object ),
755 ( void * ) messages );
757 if( NA_IS_OBJECT_ITEM( object )){
758 read_done_item_set_localized_icon( NAXML_READER( reader_data ), NA_OBJECT_ITEM( object ));
761 if( NA_IS_OBJECT_ACTION( object )){
762 read_done_action_read_profiles( NAXML_READER( reader_data ), NA_OBJECT_ACTION( object ));
765 if( NA_IS_OBJECT_PROFILE( object )){
766 read_done_profile_set_localized_label( NAXML_READER( reader_data ), NA_OBJECT_PROFILE( object ));
769 g_debug( "%s: quitting for %s at %p", thisfn, G_OBJECT_TYPE_NAME( object ), ( void * ) object );
773 * just having readen this NAObjectItem
774 * so deals with unlocalized/localized icon name/path
776 static void
777 read_done_item_set_localized_icon( NAXMLReader *reader, NAObjectItem *item )
779 gchar *icon, *unloc_icon;
781 /* deals with localized/unlocalized icon name
782 * it used to be unlocalized up to 2.29.4 included
784 icon = na_object_get_icon( item );
786 if( !icon || !strlen( icon )){
787 unloc_icon = na_object_get_icon_noloc( item );
789 if( unloc_icon && strlen( unloc_icon )){
790 na_object_set_icon( item, unloc_icon );
793 g_free( unloc_icon );
796 g_free( icon );
800 * if we have detected a pre-v2 action, then the action_read_done() function
801 * has already allocated and define the corresponding profile
802 * -> deals here with v2 and post, i.e. with profiles
804 * Also note that profiles order has been introduced in 2.29 serie
806 static void
807 read_done_action_read_profiles( NAXMLReader *reader, NAObjectAction *action )
809 GSList *order, *ip;
810 gchar *profile_id;
812 if( !na_object_get_items_count( reader->private->parms->imported )){
814 /* first attach potential ordered profiles
816 order = na_object_get_items_slist( reader->private->parms->imported );
817 for( ip = order ; ip ; ip = ip->next ){
818 read_done_action_load_profile( reader, ( const gchar * ) ip->data );
821 /* then attach unordered ones
823 while( 1 ){
824 profile_id = read_done_action_get_next_profile_id( reader );
826 if( profile_id ){
827 read_done_action_load_profile( reader, profile_id );
828 g_free( profile_id );
830 } else {
831 break;
838 * return the first profile id found in the nodes
840 static gchar *
841 read_done_action_get_next_profile_id( NAXMLReader *reader )
843 gchar *profile_id;
844 GList *ip;
846 profile_id = NULL;
848 /*g_debug( "read_done_action_get_next_profile_id: nodes=%p (count=%d)",
849 ( void * ) reader->private->nodes, g_list_length( reader->private->nodes ));*/
851 for( ip = reader->private->nodes ; ip && !profile_id ; ip = ip->next ){
852 xmlNode *parent_node = ( xmlNode * ) ip->data;
853 xmlNode *entry_node = search_for_child_node( parent_node, reader->private->root_node_str->key_entry );
854 xmlChar *text = xmlNodeGetContent( entry_node );
856 /*g_debug( "text=%s, is_profile=%s",
857 ( const gchar * ) text, is_profile_path( reader, text ) ? "True":"False" );*/
859 if( is_profile_path( reader, text )){
860 gchar *name = g_path_get_dirname(( const gchar * ) text );
861 profile_id = g_path_get_basename( name );
862 g_free( name );
864 if( na_object_get_item( reader->private->parms->imported, profile_id )){
865 g_free( profile_id );
866 profile_id = NULL;
870 xmlFree( text );
873 return( profile_id );
876 static void
877 read_done_action_load_profile( NAXMLReader *reader, const gchar *profile_id )
879 /*g_debug( "naxml_reader_read_done_action_load_profile: profile_id=%s", profile_id );*/
881 NAObjectProfile *profile = na_object_profile_new();
883 na_object_set_id( profile, profile_id );
885 na_ifactory_provider_read_item(
886 NA_IFACTORY_PROVIDER( reader->private->importer ),
887 reader,
888 NA_IFACTORY_OBJECT( profile ),
889 &reader->private->parms->messages );
893 * just having readen this NAObjectProfile
894 * so deals with unlocalized/localized desc-name
896 static void
897 read_done_profile_set_localized_label( NAXMLReader *reader, NAObjectProfile *profile )
899 gchar *descname, *unloc_descname;
901 /* deals with localized/unlocalized descname name
902 * it used to be unlocalized up to 2.29.4 included
904 descname = na_object_get_label( profile );
906 if( !descname || !strlen( descname )){
907 unloc_descname = na_object_get_label_noloc( profile );
909 if( unloc_descname && strlen( unloc_descname )){
910 na_object_set_label( profile, unloc_descname );
913 g_free( unloc_descname );
916 g_free( descname );
920 * 'key' and 'applyto' keys: check the id
921 * 'applyto' key: check for type
922 * returns set node_ok if:
923 * - each key appears is known and appears only once
924 * - there is an applyto key
926 static guint
927 schema_parse_schema_content( NAXMLReader *reader, xmlNode *schema )
929 xmlNode *iter;
930 NAXMLKeyStr *str;
931 int i;
932 guint code;
934 code = IMPORTER_CODE_OK;
936 for( iter = schema->children ; iter && code == IMPORTER_CODE_OK ; iter = iter->next ){
938 if( iter->type != XML_ELEMENT_NODE ){
939 continue;
942 str = NULL;
943 for( i = 0 ; naxml_schema_key_schema_str[i].key && !str ; ++i ){
944 if( !strxcmp( iter->name, naxml_schema_key_schema_str[i].key )){
945 str = naxml_schema_key_schema_str+i;
949 if( !str ){
950 gchar *node_list = build_key_node_list( naxml_schema_key_schema_str );
951 na_core_utils_slist_add_message( &reader->private->parms->messages,
952 ERR_NODE_UNKNOWN,
953 ( const char * ) iter->name, iter->line, node_list );
954 g_free( node_list );
955 reader->private->node_ok = FALSE;
956 continue;
959 if( str->reader_found ){
960 na_core_utils_slist_add_message( &reader->private->parms->messages,
961 ERR_NODE_ALREADY_FOUND,
962 ( const char * ) iter->name, iter->line );
963 reader->private->node_ok = FALSE;
964 continue;
967 str->reader_found = TRUE;
969 /* set the item id the first time, check after
971 if( !strxcmp( iter->name, NAXML_KEY_SCHEMA_NODE_KEY ) ||
972 !strxcmp( iter->name, NAXML_KEY_SCHEMA_NODE_APPLYTO )){
974 schema_check_for_id( reader, iter );
976 if( !reader->private->node_ok ){
977 continue;
981 /* search for the type of the item
983 if( !strxcmp( iter->name, NAXML_KEY_SCHEMA_NODE_APPLYTO )){
985 schema_check_for_type( reader, iter );
987 if( !reader->private->node_ok ){
988 continue;
993 return( code );
997 * check the id on 'key' and 'applyto' keys
999 static void
1000 schema_check_for_id( NAXMLReader *reader, xmlNode *iter )
1002 guint idx = 0;
1004 if( !strxcmp( iter->name, NAXML_KEY_SCHEMA_NODE_KEY )){
1005 idx = 1;
1008 xmlChar *text = xmlNodeGetContent( iter );
1009 gchar **path_elts = g_strsplit(( const gchar * ) text, "/", -1 );
1010 gchar *id = g_strdup( path_elts[ reader->private->root_node_str->key_length+idx-2 ] );
1011 g_strfreev( path_elts );
1012 xmlFree( text );
1014 if( reader->private->item_id ){
1015 if( strcmp( reader->private->item_id, id ) != 0 ){
1016 na_core_utils_slist_add_message( &reader->private->parms->messages,
1017 ERR_NODE_INVALID_ID,
1018 reader->private->item_id, id, iter->line );
1019 reader->private->node_ok = FALSE;
1021 } else {
1022 reader->private->item_id = g_strdup( id );
1025 g_free( id );
1029 * check 'applyto' key for 'Type'
1031 static void
1032 schema_check_for_type( NAXMLReader *reader, xmlNode *iter )
1034 xmlChar *text = xmlNodeGetContent( iter );
1036 gchar *entry = g_path_get_basename(( const gchar * ) text );
1038 if( !strcmp( entry, NAGP_ENTRY_TYPE )){
1039 reader->private->type_found = TRUE;
1040 gchar *type = get_value_from_child_node( iter->parent, NAXML_KEY_SCHEMA_NODE_DEFAULT );
1042 if( !strcmp( type, NAGP_VALUE_TYPE_ACTION )){
1043 reader->private->parms->imported = NA_OBJECT_ITEM( na_object_action_new());
1045 } else if( !strcmp( type, NAGP_VALUE_TYPE_MENU )){
1046 reader->private->parms->imported = NA_OBJECT_ITEM( na_object_menu_new());
1048 } else {
1049 na_core_utils_slist_add_message( &reader->private->parms->messages, ERR_NODE_UNKNOWN_TYPE, type, iter->line );
1050 reader->private->node_ok = FALSE;
1053 g_free( type );
1056 g_free( entry );
1057 xmlFree( text );
1060 static gchar *
1061 schema_read_value( NAXMLReader *reader, xmlNode *node, const NADataDef *def )
1063 gchar *value;
1065 if( def->localizable ){
1066 value = get_value_from_child_child_node( node, NAXML_KEY_SCHEMA_NODE_LOCALE, NAXML_KEY_SCHEMA_NODE_LOCALE_DEFAULT );
1067 } else {
1068 value = get_value_from_child_node( node, NAXML_KEY_SCHEMA_NODE_DEFAULT );
1071 /*g_debug( "name=%s, localizable=%s, value=%s", def->name, def->localizable ? "True":"False", value );*/
1072 return( value );
1076 * first run: do nothing
1077 * second run: get the id
1079 static guint
1080 dump_parse_list_parms( NAXMLReader *reader, xmlNode *node )
1082 guint code;
1084 code = IMPORTER_CODE_OK;
1086 xmlChar *path = xmlGetProp( node, ( const xmlChar * ) NAXML_KEY_DUMP_LIST_PARM_BASE );
1087 reader->private->item_id = g_path_get_basename(( const gchar * ) path );
1088 xmlFree( path );
1090 return( code );
1094 * first_run: only search for a 'Type' key, and allocate the item
1095 * second run: load data
1097 static guint
1098 dump_parse_entry_content( NAXMLReader *reader, xmlNode *entry )
1100 xmlNode *iter;
1101 NAXMLKeyStr *str;
1102 int i;
1103 guint code;
1105 code = IMPORTER_CODE_OK;
1107 for( iter = entry->children ; iter && code == IMPORTER_CODE_OK ; iter = iter->next ){
1109 if( iter->type != XML_ELEMENT_NODE ){
1110 continue;
1113 str = NULL;
1114 for( i = 0 ; naxml_dump_key_entry_str[i].key && !str ; ++i ){
1115 if( !strxcmp( iter->name, naxml_dump_key_entry_str[i].key )){
1116 str = naxml_dump_key_entry_str+i;
1120 if( !str ){
1121 gchar *node_list = build_key_node_list( naxml_dump_key_entry_str );
1122 na_core_utils_slist_add_message( &reader->private->parms->messages,
1123 ERR_NODE_UNKNOWN,
1124 ( const char * ) iter->name, iter->line, node_list );
1125 g_free( node_list );
1126 reader->private->node_ok = FALSE;
1127 continue;
1130 if( str->reader_found ){
1131 na_core_utils_slist_add_message( &reader->private->parms->messages,
1132 ERR_NODE_ALREADY_FOUND,
1133 ( const char * ) iter->name, iter->line );
1134 reader->private->node_ok = FALSE;
1135 continue;
1138 str->reader_found = TRUE;
1140 /* search for the type of the item
1142 if( !strxcmp( iter->name, NAXML_KEY_DUMP_NODE_KEY )){
1144 dump_check_for_type( reader, iter );
1146 if( !reader->private->node_ok ){
1147 continue;
1152 return( code );
1156 * check for 'Type'
1158 static void
1159 dump_check_for_type( NAXMLReader *reader, xmlNode *key_node )
1161 xmlChar *key_content = xmlNodeGetContent( key_node );
1163 if( !strxcmp( key_content, NAGP_ENTRY_TYPE )){
1164 reader->private->type_found = TRUE;
1165 gchar *type = get_value_from_child_child_node( key_node->parent, NAXML_KEY_DUMP_NODE_VALUE, NAXML_KEY_DUMP_NODE_VALUE_TYPE_STRING );
1167 if( !strcmp( type, NAGP_VALUE_TYPE_ACTION )){
1168 reader->private->parms->imported = NA_OBJECT_ITEM( na_object_action_new());
1170 } else if( !strcmp( type, NAGP_VALUE_TYPE_MENU )){
1171 reader->private->parms->imported = NA_OBJECT_ITEM( na_object_menu_new());
1173 } else {
1174 na_core_utils_slist_add_message( &reader->private->parms->messages, ERR_NODE_UNKNOWN_TYPE, type, key_node->line );
1175 reader->private->node_ok = FALSE;
1178 g_free( type );
1181 xmlFree( key_content );
1185 * string list is converted to GSList, then to a GConf string
1187 static gchar *
1188 dump_read_value( NAXMLReader *reader, xmlNode *node, const NADataDef *def )
1190 gchar *string;
1191 GSList *slist;
1192 xmlNode *value_node;
1193 xmlNode *list_node;
1194 xmlNode *vv_node;
1195 xmlChar *text;
1196 xmlNode *it;
1198 string = NULL;
1200 switch( def->type ){
1201 case NAFD_TYPE_STRING:
1202 case NAFD_TYPE_LOCALE_STRING:
1203 case NAFD_TYPE_UINT:
1204 case NAFD_TYPE_BOOLEAN:
1205 string = get_value_from_child_child_node( node, NAXML_KEY_DUMP_NODE_VALUE, NAXML_KEY_DUMP_NODE_VALUE_TYPE_STRING );
1206 break;
1208 case NAFD_TYPE_STRING_LIST:
1209 slist = NULL;
1210 value_node = search_for_child_node( node, NAXML_KEY_DUMP_NODE_VALUE );
1212 if( value_node ){
1213 list_node = search_for_child_node( value_node, NAXML_KEY_DUMP_NODE_VALUE_LIST );
1215 if( list_node ){
1216 vv_node = search_for_child_node( list_node, NAXML_KEY_DUMP_NODE_VALUE );
1218 for( it = vv_node->children ; it ; it = it->next ){
1220 if( it->type == XML_ELEMENT_NODE &&
1221 !strxcmp( it->name, NAXML_KEY_DUMP_NODE_VALUE_TYPE_STRING )){
1223 text = xmlNodeGetContent( it );
1224 slist = g_slist_append( slist, ( gchar * ) text );
1230 string = na_gconf_utils_slist_to_string( slist );
1231 na_core_utils_slist_free( slist );
1232 break;
1234 case NAFD_TYPE_POINTER:
1235 default:
1236 break;
1239 return( string );
1242 static gchar *
1243 build_key_node_list( NAXMLKeyStr *strlist )
1245 NAXMLKeyStr *next;
1247 NAXMLKeyStr *istr = strlist;
1248 GString *string = g_string_new( "" );
1250 while( istr->key ){
1251 next = istr+1;
1252 if( string->len ){
1253 if( next->key ){
1254 string = g_string_append( string, ", " );
1255 } else {
1256 string = g_string_append( string, " or " );
1259 string = g_string_append( string, istr->key );
1260 istr++;
1263 return( g_string_free( string, FALSE ));
1266 static gchar *
1267 build_root_node_list( void )
1269 RootNodeStr *next;
1271 RootNodeStr *istr = st_root_node_str;
1272 GString *string = g_string_new( "" );
1274 while( istr->root_key ){
1275 next = istr+1;
1276 if( string->len ){
1277 if( next->root_key ){
1278 string = g_string_append( string, ", " );
1279 } else {
1280 string = g_string_append( string, " or " );
1283 string = g_string_append( string, istr->root_key );
1284 istr++;
1287 return( g_string_free( string, FALSE ));
1290 static gchar *
1291 get_value_from_child_node( xmlNode *node, const gchar *child )
1293 gchar *value = NULL;
1295 xmlNode *value_node = search_for_child_node( node, child );
1296 if( value_node ){
1297 xmlChar *value_value = xmlNodeGetContent( value_node );
1298 if( value_value ){
1299 value = g_strdup(( const char * ) value_value );
1300 xmlFree( value_value );
1304 return( value );
1307 static gchar *
1308 get_value_from_child_child_node( xmlNode *node, const gchar *first, const gchar *second )
1310 gchar *value = NULL;
1312 xmlNode *first_node = search_for_child_node( node, first );
1313 if( first_node ){
1314 xmlNode *second_node = search_for_child_node( first_node, second );
1315 if( second_node ){
1316 xmlChar *value_value = xmlNodeGetContent( second_node );
1317 if( value_value ){
1318 value = g_strdup(( const char * ) value_value );
1319 xmlFree( value_value );
1324 return( value );
1327 static gboolean
1328 is_profile_path( NAXMLReader *reader, xmlChar *text )
1330 gboolean is_profile;
1331 GSList *path_slist;
1332 guint path_length;
1334 path_slist = na_core_utils_slist_from_split(( const gchar * ) text, "/" );
1335 path_length = g_slist_length( path_slist );
1337 is_profile = ( path_length == 1+reader->private->root_node_str->key_length );
1339 na_core_utils_slist_free( path_slist );
1341 return( is_profile );
1344 static guint
1345 manage_import_mode( NAXMLReader *reader )
1347 NAIImporterManageImportModeParms parms;
1348 guint code;
1350 parms.version = 1;
1351 parms.imported = reader->private->parms->imported;
1352 parms.check_fn = reader->private->parms->check_fn;
1353 parms.check_fn_data = reader->private->parms->check_fn_data;
1354 parms.ask_fn = reader->private->parms->ask_fn;
1355 parms.ask_fn_data = reader->private->parms->ask_fn_data;
1356 parms.asked_mode = reader->private->parms->asked_mode;
1357 parms.messages = reader->private->parms->messages;
1359 code = na_iimporter_manage_import_mode( &parms );
1361 reader->private->parms->exist = parms.exist;
1362 reader->private->parms->import_mode = parms.import_mode;
1364 return( code );
1367 static void
1368 publish_undealt_nodes( NAXMLReader *reader )
1370 GList *iter;
1371 xmlChar *text;
1373 g_debug( "naxml_reader_publish_undealt_nodes: count=%d", g_list_length( reader->private->nodes ));
1375 for( iter = reader->private->nodes ; iter ; iter = iter->next ){
1376 xmlNode *node = ( xmlNode * ) iter->data;
1378 if( !g_list_find( reader->private->dealt, node )){
1379 text = xmlNodeGetContent( node );
1380 na_core_utils_slist_add_message( &reader->private->parms->messages, WARN_UNDEALT_NODE, ( const gchar * ) text, node->line );
1381 xmlFree( text );
1387 * data are reset before first run on nodes for an item
1389 static void
1390 reset_node_data( NAXMLReader *reader )
1392 int i;
1394 for( i=0 ; naxml_schema_key_schema_str[i].key ; ++i ){
1395 naxml_schema_key_schema_str[i].reader_found = FALSE;
1398 for( i=0 ; naxml_dump_key_entry_str[i].key ; ++i ){
1399 naxml_dump_key_entry_str[i].reader_found = FALSE;
1402 reader->private->node_ok = TRUE;
1405 static xmlNode *
1406 search_for_child_node( xmlNode *node, const gchar *key )
1408 xmlNode *iter;
1410 for( iter = node->children ; iter ; iter = iter->next ){
1411 if( iter->type == XML_ELEMENT_NODE ){
1412 if( !strxcmp( iter->name, key )){
1413 return( iter );
1418 return( NULL );
1422 * note that up to v 1.10 included, key check was made via a call to
1423 * g_ascii_strncasecmp, which was doubly wrong:
1424 * - because XML is case sensitive by definition
1425 * - because this did not detect a key longer that the reference.
1427 static int
1428 strxcmp( const xmlChar *a, const char *b )
1430 xmlChar *xb = xmlCharStrdup( b );
1431 int ret = xmlStrcmp( a, xb );
1432 xmlFree( xb );
1433 return( ret );