qsf-xml.c

00001 /***************************************************************************
00002  *            qsf-xml.c
00003  *
00004  *  Fri Nov 26 19:29:47 2004
00005  *  Copyright  2004,2005,2006  Neil Williams  <linux@codehelp.co.uk>
00006  *
00007  ****************************************************************************/
00008 /*
00009  *  This program is free software; you can redistribute it and/or modify
00010  *  it under the terms of the GNU General Public License as published by
00011  *  the Free Software Foundation; either version 2 of the License, or
00012  *  (at your option) any later version.
00013  *
00014  *  This program is distributed in the hope that it will be useful,
00015  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  *  GNU General Public License for more details.
00018  *
00019  *  You should have received a copy of the GNU General Public License
00020  *  along with this program; if not, write to the Free Software
00021  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00022  */
00023 
00024 #include "config.h"
00025 #include <glib.h>
00026 #include <libxml/xmlversion.h>
00027 #include <libxml/xmlmemory.h>
00028 #include <libxml/tree.h>
00029 #include <libxml/parser.h>
00030 #include <libxml/xmlschemas.h>
00031 #include "qof.h"
00032 #include "qof-backend-qsf.h"
00033 #include "qsf-dir.h"
00034 #include "qsf-xml.h"
00035 
00036 static QofLogModule log_module = QOF_MOD_QSF;
00037 
00038 gint
00039 qsf_compare_tag_strings (const xmlChar * node_name, gchar * tag_name)
00040 {
00041     return xmlStrcmp (node_name, (const xmlChar *) tag_name);
00042 }
00043 
00044 gint
00045 qsf_strings_equal (const xmlChar * node_name, gchar * tag_name)
00046 {
00047     if (0 == qsf_compare_tag_strings (node_name, tag_name))
00048     {
00049         return 1;
00050     }
00051     return 0;
00052 }
00053 
00054 gint
00055 qsf_is_element (xmlNodePtr a, xmlNsPtr ns, gchar * c)
00056 {
00057     g_return_val_if_fail (a != NULL, 0);
00058     g_return_val_if_fail (ns != NULL, 0);
00059     g_return_val_if_fail (c != NULL, 0);
00060     if ((a->ns == ns) && (a->type == XML_ELEMENT_NODE) &&
00061         qsf_strings_equal (a->name, c))
00062     {
00063         return 1;
00064     }
00065     return 0;
00066 }
00067 
00068 gint
00069 qsf_check_tag (QsfParam * params, gchar * qof_type)
00070 {
00071     return qsf_is_element (params->child_node, params->qsf_ns, qof_type);
00072 }
00073 
00074 gboolean
00075 qsf_is_valid (const gchar * schema_dir, const gchar * schema_filename,
00076     xmlDocPtr doc)
00077 {
00078     xmlSchemaParserCtxtPtr qsf_schema_file;
00079     xmlSchemaPtr qsf_schema;
00080     xmlSchemaValidCtxtPtr qsf_context;
00081     gchar *schema_path;
00082     gint result;
00083 
00084     g_return_val_if_fail (doc || schema_filename, FALSE);
00085     schema_path = g_strdup_printf ("%s/%s", schema_dir, schema_filename);
00086     qsf_schema_file = xmlSchemaNewParserCtxt (schema_path);
00087     qsf_schema = xmlSchemaParse (qsf_schema_file);
00088     qsf_context = xmlSchemaNewValidCtxt (qsf_schema);
00089     result = xmlSchemaValidateDoc (qsf_context, doc);
00090     xmlSchemaFreeParserCtxt (qsf_schema_file);
00091     xmlSchemaFreeValidCtxt (qsf_context);
00092     xmlSchemaFree (qsf_schema);
00093     g_free (schema_path);
00094     if (result == 0)
00095     {
00096         return TRUE;
00097     }
00098     return FALSE;
00099 }
00100 
00101 void
00102 qsf_valid_foreach (xmlNodePtr parent, QsfValidCB cb,
00103     struct QsfNodeIterate *qsfiter, QsfValidator * valid)
00104 {
00105     xmlNodePtr cur_node;
00106 
00107     qsfiter->v_fcn = &cb;
00108     for (cur_node = parent->children; cur_node != NULL;
00109         cur_node = cur_node->next)
00110     {
00111         cb (cur_node, qsfiter->ns, valid);
00112     }
00113 }
00114 
00115 void
00116 qsf_node_foreach (xmlNodePtr parent, QsfNodeCB cb,
00117     struct QsfNodeIterate *qsfiter, QsfParam * params)
00118 {
00119     xmlNodePtr cur_node;
00120 
00121     if (!parent)
00122         return;
00123     g_return_if_fail (params);
00124     g_return_if_fail (qsfiter->ns);
00125     qsfiter->fcn = &cb;
00126     for (cur_node = parent->children; cur_node != NULL;
00127         cur_node = cur_node->next)
00128     {
00129         cb (cur_node, qsfiter->ns, params);
00130     }
00131 }
00132 
00133 void
00134 qsf_object_validation_handler (xmlNodePtr child, xmlNsPtr ns,
00135     QsfValidator * valid)
00136 {
00137     xmlNodePtr cur_node;
00138     xmlChar *object_declaration;
00139     guint count;
00140     QsfStatus type;
00141     gboolean is_registered;
00142 
00143     count = 0;
00144     type = QSF_NO_OBJECT;
00145     is_registered = FALSE;
00146     for (cur_node = child->children; cur_node != NULL;
00147         cur_node = cur_node->next)
00148     {
00149         if (qsf_is_element (cur_node, ns, QSF_OBJECT_TAG))
00150         {
00151             object_declaration =
00152                 xmlGetProp (cur_node, BAD_CAST QSF_OBJECT_TYPE);
00153             is_registered = qof_class_is_registered (object_declaration);
00154             if (is_registered)
00155             {
00156                 type = QSF_REGISTERED_OBJECT;
00157             }
00158             else
00159             {
00160                 type = QSF_DEFINED_OBJECT;
00161             }
00162             xmlFree (object_declaration);
00163             count = g_hash_table_size (valid->object_table);
00164             g_hash_table_insert (valid->object_table, object_declaration,
00165                 GINT_TO_POINTER (type));
00166             /* if insert was successful - i.e. object is unique so far */
00167             if (g_hash_table_size (valid->object_table) > count)
00168             {
00169                 valid->valid_object_count++;
00170                 if (is_registered)
00171                 {
00172                     valid->qof_registered_count++;
00173                 }
00174             }
00175         }
00176     }
00177 }
00178 
00179 gboolean
00180 is_our_qsf_object (const gchar * path)
00181 {
00182     xmlDocPtr doc;
00183     struct QsfNodeIterate qsfiter;
00184     xmlNodePtr object_root;
00185     QsfValidator valid;
00186     gint table_count;
00187 
00188     g_return_val_if_fail ((path != NULL), FALSE);
00189     doc = xmlParseFile (path);
00190     if (doc == NULL)
00191     {
00192         return FALSE;
00193     }
00194     if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
00195     {
00196         PINFO (" validation failed %s %s %s", QSF_SCHEMA_DIR,
00197             QSF_OBJECT_SCHEMA, path);
00198         return FALSE;
00199     }
00200     object_root = xmlDocGetRootElement (doc);
00201     /* check that all objects in the file are already registered in QOF */
00202     valid.object_table = g_hash_table_new (g_str_hash, g_str_equal);
00203     valid.qof_registered_count = 0;
00204     valid.valid_object_count = 0;
00205     qsfiter.ns = object_root->ns;
00206     qsf_valid_foreach (object_root, qsf_object_validation_handler, 
00207         &qsfiter, &valid);
00208     table_count = g_hash_table_size (valid.object_table);
00209     g_hash_table_destroy (valid.object_table);
00210     xmlFreeDoc (doc);
00211     if (table_count == valid.qof_registered_count)
00212     {
00213         return TRUE;
00214     }
00215     return FALSE;
00216 }
00217 
00218 gboolean
00219 is_qsf_object (const gchar * path)
00220 {
00221     xmlDocPtr doc;
00222 
00223     g_return_val_if_fail ((path != NULL), FALSE);
00224     if (path == NULL)
00225     {
00226         return FALSE;
00227     }
00228     doc = xmlParseFile (path);
00229     if (doc == NULL)
00230     {
00231         return FALSE;
00232     }
00233     if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
00234     {
00235         return FALSE;
00236     }
00237     /* Note cannot test against a map here, so if the file is valid QSF,
00238        accept it and work out the details later. */
00239     return TRUE;
00240 }
00241 
00242 gboolean
00243 is_our_qsf_object_be (QsfParam * params)
00244 {
00245     xmlDocPtr doc;
00246     struct QsfNodeIterate qsfiter;
00247     xmlNodePtr object_root;
00248     QsfValidator valid;
00249     gint table_count;
00250 
00251     g_return_val_if_fail ((params != NULL), FALSE);
00252     if (params->filepath == NULL)
00253     {
00254         qof_error_set_be (params->be, qof_error_register
00255         (_("The QSF XML file '%s' could not be found."), TRUE));
00256         return FALSE;
00257     }
00258     if (params->file_type != QSF_UNDEF)
00259     {
00260         return FALSE;
00261     }
00262     doc = xmlParseFile (params->filepath);
00263     if (doc == NULL)
00264     {
00265         qof_error_set_be (params->be, qof_error_register
00266         (_("There was an error parsing the file '%s'."), TRUE));
00267         return FALSE;
00268     }
00269     if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
00270     {
00271         qof_error_set_be (params->be, qof_error_register
00272         (_("Invalid QSF Object file! The QSF object file '%s' "
00273         " failed to validate  against the QSF object schema. "
00274         "The XML structure of the file is either not well-formed "
00275         "or the file contains illegal data."), TRUE));
00276         xmlFreeDoc (doc);
00277         return FALSE;
00278     }
00279     params->file_type = IS_QSF_OBJ;
00280     object_root = xmlDocGetRootElement (doc);
00281     xmlFreeDoc (doc);
00282     valid.object_table = g_hash_table_new (g_str_hash, g_str_equal);
00283     valid.qof_registered_count = 0;
00284     qsfiter.ns = object_root->ns;
00285     qsf_valid_foreach (object_root, qsf_object_validation_handler, 
00286         &qsfiter, &valid);
00287     table_count = g_hash_table_size (valid.object_table);
00288     if (table_count == valid.qof_registered_count)
00289     {
00290         g_hash_table_destroy (valid.object_table);
00291         return TRUE;
00292     }
00293     g_hash_table_destroy (valid.object_table);
00294     qof_error_set_be (params->be, params->err_nomap);
00295     return FALSE;
00296 }
00297 
00298 gboolean
00299 is_qsf_object_be (QsfParam * params)
00300 {
00301     gboolean result;
00302     xmlDocPtr doc;
00303     GList *maps;
00304     gchar *path;
00305 
00306     g_return_val_if_fail ((params != NULL), FALSE);
00307     path = g_strdup (params->filepath);
00308     if (path == NULL)
00309     {
00310         qof_error_set_be (params->be, qof_error_register
00311         (_("The QSF XML file '%s' could not be found."), TRUE));
00312         return FALSE;
00313     }
00314     /* skip validation if is_our_qsf_object has already been called. */
00315 /*  if (ERR_QSF_INVALID_OBJ == qof_backend_get_error (params->be))
00316     {
00317         return FALSE;
00318     }*/
00319     if (params->file_type == QSF_UNDEF)
00320     {
00321         doc = xmlParseFile (path);
00322         if (doc == NULL)
00323         {
00324             qof_error_set_be (params->be, qof_error_register
00325             (_("There was an error parsing the file '%s'."), TRUE));
00326             return FALSE;
00327         }
00328         if (TRUE != qsf_is_valid (QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc))
00329         {
00330             qof_error_set_be (params->be, qof_error_register
00331             (_("Invalid QSF Object file! The QSF object file '%s' "
00332             " failed to validate  against the QSF object schema. "
00333             "The XML structure of the file is either not well-formed "
00334             "or the file contains illegal data."), TRUE));
00335             return FALSE;
00336         }
00337     }
00338     result = FALSE;
00339     /* retrieve list of maps from config frame. */
00340     for (maps = params->map_files; maps; maps = maps->next)
00341     {
00342         QofErrorId err;
00343         result = is_qsf_object_with_map_be (maps->data, params);
00344         err = qof_error_check_be (params->be);
00345         if ((err == QOF_SUCCESS) && result)
00346         {
00347             params->map_path = maps->data;
00348             PINFO ("map chosen = %s", params->map_path);
00349             break;
00350         }
00351     }
00352     return result;
00353 }
00354 
00355 static void
00356 qsf_supported_data_types (gpointer type, gpointer user_data)
00357 {
00358     QsfParam *params;
00359 
00360     g_return_if_fail (user_data != NULL);
00361     g_return_if_fail (type != NULL);
00362     params = (QsfParam *) user_data;
00363     if (qsf_is_element (params->param_node, params->qsf_ns,
00364             (gchar *) type))
00365     {
00366         g_hash_table_insert (params->qsf_parameter_hash,
00367             xmlGetProp (params->param_node,
00368                 BAD_CAST QSF_OBJECT_TYPE), params->param_node);
00369     }
00370 }
00371 
00372 static void
00373 qsf_parameter_handler (xmlNodePtr child, xmlNsPtr qsf_ns,
00374     QsfParam * params)
00375 {
00376     /* spurious */
00377     if (!qsf_ns)
00378         return;
00379     params->param_node = child;
00380     g_slist_foreach (params->supported_types, qsf_supported_data_types,
00381         params);
00382 }
00383 
00384 void
00385 qsf_object_node_handler (xmlNodePtr child, xmlNsPtr qsf_ns,
00386     QsfParam * params)
00387 {
00388     struct QsfNodeIterate qsfiter;
00389     QsfObject *object_set;
00390     gchar *tail, *object_count_s;
00391     gint64 c;
00392 
00393     g_return_if_fail (child != NULL);
00394     g_return_if_fail (qsf_ns != NULL);
00395     params->qsf_ns = qsf_ns;
00396     if (qsf_is_element (child, qsf_ns, QSF_OBJECT_TAG))
00397     {
00398         params->qsf_parameter_hash = NULL;
00399         c = 0;
00400         object_set = g_new (QsfObject, 1);
00401         params->object_set = object_set;
00402         object_set->object_count = 0;
00403         object_set->parameters =
00404             g_hash_table_new (g_str_hash, g_str_equal);
00405         object_set->object_type = ((gchar *) xmlGetProp (child,
00406                 BAD_CAST QSF_OBJECT_TYPE));
00407         object_count_s = ((gchar *) xmlGetProp (child,
00408                 BAD_CAST QSF_OBJECT_COUNT));
00409         if (object_count_s)
00410         {
00411             c = (gint64) strtol (object_count_s, &tail, 0);
00412             object_set->object_count = (gint) c;
00413             g_free (object_count_s);
00414         }
00415         params->qsf_object_list =
00416             g_list_prepend (params->qsf_object_list, object_set);
00417         qsfiter.ns = qsf_ns;
00418         params->qsf_parameter_hash = object_set->parameters;
00419         qsf_node_foreach (child, qsf_parameter_handler, &qsfiter, params);
00420     }
00421 }
00422 
00423 void
00424 qsf_book_node_handler (xmlNodePtr child, xmlNsPtr ns, QsfParam * params)
00425 {
00426     gchar *book_count_s, *tail;
00427     gint book_count;
00428     xmlNodePtr child_node;
00429     struct QsfNodeIterate qsfiter;
00430     gchar *buffer;
00431     GUID book_guid;
00432 
00433     g_return_if_fail (child);
00434     g_return_if_fail (params);
00435     ENTER (" child=%s", child->name);
00436     if (qsf_is_element (child, ns, QSF_BOOK_TAG))
00437     {
00438         book_count_s =
00439             (gchar *) xmlGetProp (child, BAD_CAST QSF_BOOK_COUNT);
00440         if (book_count_s)
00441         {
00442             book_count = (gint) strtol (book_count_s, &tail, 0);
00443             /* More than one book not currently supported. */
00444             g_free (book_count_s);
00445             g_return_if_fail (book_count == 1);
00446         }
00447         qsfiter.ns = ns;
00448         child_node = child->children->next;
00449         if (qsf_is_element (child_node, ns, QSF_BOOK_GUID))
00450         {
00451             DEBUG (" trying to set book GUID");
00452             buffer = BAD_CAST xmlNodeGetContent (child_node);
00453             g_return_if_fail (TRUE == string_to_guid (buffer, &book_guid));
00454             qof_entity_set_guid ((QofEntity *) params->book, &book_guid);
00455             xmlNewChild (params->output_node, params->qsf_ns,
00456                 BAD_CAST QSF_BOOK_GUID, BAD_CAST buffer);
00457             xmlFree (buffer);
00458         }
00459         qsf_node_foreach (child, qsf_object_node_handler, &qsfiter, params);
00460     }
00461     LEAVE (" ");
00462 }

Generated on Thu Jan 31 22:50:26 2008 for QOF by  doxygen 1.5.4