#include <stdio.h>
#include <stdlib.h>
#include <regex.h>
#include <time.h>
#include "qof.h"
#include <libintl.h>
Go to the source code of this file.
Data Structures | |
| struct | qsf_object_set |
| Holds a description of the QofObject. More... | |
| struct | qsf_metadata |
| QSF Parameters. More... | |
| struct | qsf_validates |
| Validation metadata. More... | |
| struct | qsf_node_iterate |
| One iterator, two typedefs. More... | |
Defines | |
| #define | _(String) dgettext (GETTEXT_PACKAGE, String) |
| #define | QSF_QOF_VERSION QOF_OBJECT_VERSION |
| #define | QSF_XSD_TIME QOF_UTC_DATE_FORMAT |
| #define | QSF_XML_BOOLEAN_TEST "true" |
| #define | QSF_OBJECT_SCHEMA "qsf-object.xsd.xml" |
| #define | QSF_MAP_SCHEMA "qsf-map.xsd.xml" |
QSF Object XML | |
| #define | QSF_ROOT_TAG "qof-qsf" |
| #define | QSF_DEFAULT_NS "http://qof.sourceforge.net/" |
| #define | QSF_DATE_LENGTH MAX_DATE_LENGTH |
| #define | QSF_BOOK_TAG "book" |
| #define | QSF_BOOK_GUID "book-guid" |
| #define | QSF_BOOK_COUNT "count" |
| #define | QSF_OBJECT_TAG "object" |
| #define | QSF_OBJECT_TYPE "type" |
| #define | QSF_OBJECT_COUNT "count" |
| #define | QSF_XML_VERSION "1.0" |
Representing KVP as XML | |
<kvp type="kvp" path="/from-sched-xaction" value="guid">c858b9a3235723b55bc1179f0e8c1322</kvp> A kvp type KVP parameter located at $path containing a GncGUID $value. The relevance of type="kvp" won't be evident in GnuCash, they all use "kvp".
A non-GnuCash example helps: <kvp type="pilot_addr_kvp" path="/user/name" value="guid">c858b9a3235723b55bc1179f0e8c1322</kvp> A pilot_addr_kvp type KVP parameter located at /user/name containing a guid value. | |
| #define | QSF_OBJECT_KVP "path" |
| #define | QSF_OBJECT_VALUE "value" |
QSF Map XML | |
| #define | MAP_ROOT_TAG "qsf-map" |
| #define | MAP_DEFINITION_TAG "definition" |
| #define | MAP_DEFINE_TAG "define" |
| #define | MAP_ITERATE_ATTR "foreach" |
| #define | MAP_DEFAULT_TAG "default" |
| #define | MAP_OBJECT_TAG "object" |
| #define | MAP_CALCULATE_TAG "calculate" |
| #define | MAP_QOF_VERSION "qof_version" |
| #define | MAP_NAME_ATTR "name" |
| #define | MAP_TYPE_ATTR "type" |
| #define | MAP_VALUE_ATTR "value" |
| #define | MAP_OBJECT_ATTR "object" |
| #define | MAP_E_TYPE "e_type" |
| #define | MAP_ENUM_TYPE "enum" |
| #define | QSF_BOOLEAN_DEFAULT "boolean" |
| A specific boolean default for this map. | |
| #define | QSF_CONDITIONAL "if" |
| #define | QSF_CONDITIONAL_SET "set" |
| #define | QSF_CONDITIONAL_ELSE "else" |
| #define | QSF_OPTION "option" |
| #define | QSF_FORMATTING_OPTION "format" |
Typedefs | |
| typedef struct qsf_object_set | qsf_objects |
| Holds a description of the QofObject. | |
| typedef struct qsf_metadata | qsf_param |
| QSF Parameters. | |
| typedef struct qsf_validates | qsf_validator |
| Validation metadata. | |
Enumerations | |
| enum | qsf_type { QSF_UNDEF = 0, IS_QSF_MAP, IS_QSF_OBJ, HAVE_QSF_MAP, OUR_QSF_OBJ } |
Functions | |
| gint | qsf_compare_tag_strings (const xmlChar *node_name, gchar *tag_name) |
| shorthand function | |
| gint | qsf_strings_equal (const xmlChar *node_name, gchar *tag_name) |
| shorthand function | |
| gint | qsf_is_element (xmlNodePtr a, xmlNsPtr ns, gchar *c) |
| shorthand function | |
| gint | qsf_check_tag (qsf_param *params, gchar *qof_type) |
| shorthand function | |
| void | qsf_object_validation_handler (xmlNodePtr child, xmlNsPtr ns, qsf_validator *valid) |
| Checks all incoming objects for QOF registration. | |
| gboolean | qsf_is_valid (const gchar *schema_dir, const gchar *schema_filename, xmlDocPtr doc) |
| Compares an xmlDoc in memory against the schema file. | |
| GList ** | qsf_map_prepare_list (GList **maps) |
| Prepare the default list of maps. | |
| void | qsf_book_node_handler (xmlNodePtr child, xmlNsPtr qsf_ns, qsf_param *params) |
| Book and book-guid node handler. | |
| KvpValue * | string_to_kvp_value (const gchar *content, KvpValueType type) |
| Convert a string value into KvpValue. | |
| void | qsf_valid_foreach (xmlNodePtr parent, qsf_validCB cb, struct qsf_node_iterate *iter, qsf_validator *valid) |
| void | qsf_node_foreach (xmlNodePtr parent, qsf_nodeCB cb, struct qsf_node_iterate *iter, qsf_param *params) |
| xmlDocPtr | qsf_object_convert (xmlDocPtr mapDoc, xmlNodePtr qsf_root, qsf_param *params) |
| Convert between QSF objects. | |
| void | qsf_object_node_handler (xmlNodePtr child, xmlNsPtr qsf_ns, qsf_param *params) |
Definition in file qsf-xml.h.
| #define MAP_CALCULATE_TAG "calculate" |
One calculation for every parameter that needs to be set.
QSF follows the same rule as qof_book_merge. Only if a getter and a setter function are defined for a parameter is it available to QSF. If a QofAccessFunc and QofSetterFunc are both defined for any QofObject parameter, that parameter MUST be calculated in any map that defines that object.
| #define MAP_DEFAULT_TAG "default" |
User editable defaults for data not available within the available QSF objects.
Some defaults will relate to how to format descriptive dates, whether discount should be considered, which account to use for certain QSF data from applications that do not use accounts.
Some defaults are pre-defined and cannot be over-written:
Attributes (All are mandatory):
name The text name for this default. Certain pre-defined defaults exist but user- or map-defined defaults can have any unique text name. Spaces are NOT allowed, use undersccores instead. The value of name must not duplicate any existing default, define, object or parameter unless the special type, enum, is used.
type QOF_TYPE - must be one of the recognised QOF data types for the qof_version in use or the special type, enum.
value Text representation of the required value. For numeric, use the format [0-9]?/[0-9]?
QSF deals with partial QofBooks - each object is fully described but the book does not have to contain any specific object types or have any particular structure. To merge partial books into usual QofBook data sources, the map must deal with entities that need to be referenced in the target QofBook but which simply do not exist in the QofBook used to generate the QSF. e.g. pilot-link knows nothing of Accounts yet when QSF creates a gncInvoice from qof-datebook, gncInvoice needs to know the GncGUID of certain accounts in the target QofBook. This is handled in the map by specifying the name of the account as a default for that map. When imported, the QSF QofBackend looks up the object required using the name of the parameter to obtain the parameter type. This is the only situation where QSF converts between QOF data types. A string description of the required object is converted to the GncGUID for that specific entity. The map cannot contain the GncGUID as it is generic and used by multiple users.
| #define MAP_DEFINE_TAG "define" |
| #define MAP_DEFINITION_TAG "definition" |
Second level container for defined objects
Attributes: qof_version - Taken from the QOF_OBJECT_VERSION macro in QOF, At the time of QSF development, QOF_OBJECT_VERSION is defined as 3. All QSF maps and QSF objects must use the same qof_version which in turn must match the QOF_OBJECT_VERSION for the QOF library in use by the calling process.
No text content allowed.
| #define MAP_E_TYPE "e_type" |
Validates the objects defined in the map
The e_type will be used to match incoming QSF objects with the relevant QSF map. The value of the e_type must be the value of the e_type for that object in the originating QOF application. The define tag must contain the value of the description of the same object in the same originating QOF application.
| #define MAP_ENUM_TYPE "enum" |
| #define MAP_ITERATE_ATTR "foreach" |
| #define MAP_NAME_ATTR "name" |
| #define MAP_OBJECT_ATTR "object" |
| #define MAP_OBJECT_TAG "object" |
Contains all the calculations to make one object from others.
Note that creating an object for the import application can involve using data from more than one QSF object, as well as defaults and lookups in the import application itself. Conditionals, simple arithmetic and date/time formatting options are also available.
| #define MAP_QOF_VERSION "qof_version" |
| #define MAP_ROOT_TAG "qsf-map" |
| #define MAP_TYPE_ATTR "type" |
| #define MAP_VALUE_ATTR "value" |
The value of the tag, used in defaults and calculations.
The value of a default is a string representation of the value to be inserted into the calculation where the default is used.
The value of a calculation is the name of the parameter that will be set by that calculation.
| #define QSF_BOOK_COUNT "count" |
| #define QSF_BOOK_GUID "book-guid" |
| #define QSF_BOOK_TAG "book" |
| #define QSF_CONDITIONAL "if" |
child of calculate.
Conditionals can reference objects as if within the original application. In operation, the map is overlaid across both sets of defined objects, an import object in the source application and an output object for the destination object. The current import and output QSF objects are therefore always available to the map. Conditionals can reference parameter as well as object values.
| #define QSF_CONDITIONAL_ELSE "else" |
Alternative
if(){} else{} is also supported. Nesting of conditionals causes problems for validating the final map against any sensible XML Schema and a map that does not validate will be rejected. When editing conditionals in a QSF map, ALWAYS validate the map using xmllint. If necessary, define a variable at the foot of the definitions block, using a similar syntax to a default, then use that variable in another conditional
variable name="my_rate" type="numeric" value="0/1"
The syntax for xmllint is:
xmllint --schema <schema file> <qsf-file>
Use the qsf-object.xsd.xml schema for objects and qsf-map.xsd.xml for map files.
e.g. xmllint --schema qsf-object.xsd.xml --noout qof-qsf.xml
| #define QSF_CONDITIONAL_SET "set" |
Assignment statement
Map assignments can use the native values within the output object. The output object must support setting the relevant parameter using the value exactly as given in the map because the relevant set() function will be called using this value. This may reduce the readability of the map but the relevant application could also be modified to support a more readable set function.
| #define QSF_DATE_LENGTH MAX_DATE_LENGTH |
Max length of QSF_XSD_TIME.
MAX_DATE_LENGTH itself is defined in gnc-date.h
| #define QSF_DEFAULT_NS "http://qof.sourceforge.net/" |
| #define QSF_FORMATTING_OPTION "format" |
How to format dates/times
When the QSF map uses a date/time value as a string, the formatting can be adjusted to personal preference. format will only be evaluated if the calculated parameter is a QOF_TYPE_STRING - any format attributes on other data types will be ignored.
| #define QSF_MAP_SCHEMA "qsf-map.xsd.xml" |
| #define QSF_OBJECT_COUNT "count" |
| #define QSF_OBJECT_KVP "path" |
| #define QSF_OBJECT_SCHEMA "qsf-object.xsd.xml" |
| #define QSF_OBJECT_TAG "object" |
| #define QSF_OBJECT_TYPE "type" |
| #define QSF_OPTION "option" |
| #define QSF_QOF_VERSION QOF_OBJECT_VERSION |
| #define QSF_XML_BOOLEAN_TEST "true" |
| #define QSF_XSD_TIME QOF_UTC_DATE_FORMAT |
xsd:dateTime format in coordinated universal time, UTC.
You can reproduce the string from the GNU/Linux command line using the date utility:
date -u +Y-m-dTH:M:SZ
2004-12-12T23:39:11Z
The datestring must be timezone independent and include all specified fields.
Remember to use gmtime() NOT localtime()!. From the command line, use the -u switch with the date command: date -u
To generate a timestamp based on a real time, use the qsf_time_now and qsf_time_string defaults.
qsf_time_now : Format: QOF_TYPE_DATE. The current time taken from the moment the default is read into a QSF object at runtime.
qsf_time_string : Format: QOF_TYPE_STRING. The current timestamp taken from the moment the default is read into a QSF object at runtime. This form is used when the output parameter needs a formatted date string, not an actual date object. The format is determined by the optional format attribute of the set tag which takes the same operators as the GNU C Library for strftime() and output may therefore be dependent on the locale of the calling process - take care. Default value is F, used when qsf_time_string is set without the format attribute.
Both defaults use UTC.
| typedef void(* qsf_nodeCB)(xmlNodePtr, xmlNsPtr, qsf_param *) |
map and qsf object callback
This callback cannot do both the map and the validation tasks because validation sometimes needs to be done without qsf_params.
e.g. when selecting which backend should be used for a particular data source where two or more backends share the same access_method.
| typedef struct qsf_object_set qsf_objects |
Holds a description of the QofObject.
Used when converting QOF objects from another application. The incoming, unknown, objects need to be stored prior to conversion. This allows many-to-many conversions where an invoice can receive data from an incoming expense AND datebook and use data from an incoming contacts object to lookup the customer for the invoice.
| typedef struct qsf_metadata qsf_param |
QSF Parameters.
This struct is a catch-all for all parameters required for various stages of the process. There are lots of elements here that will finally be removed.
| typedef struct qsf_validates qsf_validator |
Validation metadata.
The validation is a separate parse with separate data. This is used to determine which backend should load the data.
| typedef void(* qsf_validCB)(xmlNodePtr, xmlNsPtr, qsf_validator *) |
| enum qsf_type |
Definition at line 41 of file qsf-xml.h.
00042 { 00043 QSF_UNDEF = 0, 00044 IS_QSF_MAP, 00045 IS_QSF_OBJ, 00046 HAVE_QSF_MAP, 00047 OUR_QSF_OBJ, 00048 } qsf_type;
| gboolean is_our_qsf_object | ( | const gchar * | path | ) |
Validate a QSF file.
| path | Absolute or relative path to the file to be validated |
Files that pass the test can be imported into the QOF appliction without the need for a QSF map.
Definition at line 155 of file qsf-xml.c.
00156 { 00157 xmlDocPtr doc; 00158 struct qsf_node_iterate iter; 00159 xmlNodePtr object_root; 00160 qsf_validator valid; 00161 gint table_count; 00162 00163 g_return_val_if_fail((path != NULL), FALSE); 00164 doc = xmlParseFile(path); 00165 if (doc == NULL) 00166 { 00167 return FALSE; 00168 } 00169 if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc)) 00170 { 00171 PINFO (" validation failed %s %s %s", QSF_SCHEMA_DIR, 00172 QSF_OBJECT_SCHEMA, path); 00173 xmlFreeDoc(doc); 00174 return FALSE; 00175 } 00176 object_root = xmlDocGetRootElement(doc); 00177 /* check that all objects in the file are already registered in QOF */ 00178 valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal); 00179 valid.qof_registered_count = 0; 00180 valid.valid_object_count = 0; 00181 iter.ns = object_root->ns; 00182 qsf_valid_foreach(object_root, qsf_object_validation_handler, &iter, &valid); 00183 table_count = g_hash_table_size(valid.validation_table); 00184 g_hash_table_destroy(valid.validation_table); 00185 if (table_count == valid.qof_registered_count) 00186 { 00187 return TRUE; 00188 } 00189 return FALSE; 00190 }
| gboolean is_our_qsf_object_be | ( | qsf_param * | params | ) |
Validate a QSF file and determine type.
| params | Pointer to qsf_param context |
Files that pass the test can be imported into the QOF appliction without the need for a QSF map.
Definition at line 217 of file qsf-xml.c.
00218 { 00219 xmlDocPtr doc; 00220 struct qsf_node_iterate iter; 00221 xmlNodePtr object_root; 00222 qsf_validator valid; 00223 gint table_count; 00224 gchar *path; 00225 00226 g_return_val_if_fail((params != NULL), FALSE); 00227 path = g_strdup(params->filepath); 00228 if (path == NULL) 00229 { 00230 qof_backend_set_error(params->be, ERR_FILEIO_FILE_NOT_FOUND); 00231 return FALSE; 00232 } 00233 if (params->file_type != QSF_UNDEF) 00234 { 00235 return FALSE; 00236 } 00237 doc = xmlParseFile(path); 00238 if (doc == NULL) 00239 { 00240 qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR); 00241 return FALSE; 00242 } 00243 if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc)) 00244 { 00245 qof_backend_set_error(params->be, ERR_QSF_INVALID_OBJ); 00246 return FALSE; 00247 } 00248 params->file_type = IS_QSF_OBJ; 00249 object_root = xmlDocGetRootElement(doc); 00250 valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal); 00251 valid.qof_registered_count = 0; 00252 iter.ns = object_root->ns; 00253 qsf_valid_foreach(object_root, qsf_object_validation_handler, &iter, &valid); 00254 table_count = g_hash_table_size(valid.validation_table); 00255 if (table_count == valid.qof_registered_count) 00256 { 00257 g_hash_table_destroy(valid.validation_table); 00258 qof_backend_set_error(params->be, ERR_BACKEND_NO_ERR); 00259 return TRUE; 00260 } 00261 g_hash_table_destroy(valid.validation_table); 00262 qof_backend_set_error(params->be, ERR_QSF_NO_MAP); 00263 return FALSE; 00264 }
| gboolean is_qsf_map | ( | const gchar * | path | ) |
Validate a QSF map file.
| path | Absolute or relative path to the file to be validated |
The file is validated aginst the QSF map schema, qsf-map.xsd.xsml. This function is called by is_qsf_object. If called directly, the map file is validated and closed, no data is retrieved. QSF maps do not contain user data but are used to import QSF object files from other applications.
Definition at line 330 of file qsf-xml-map.c.
00331 { 00332 xmlDocPtr doc; 00333 struct qsf_node_iterate iter; 00334 qsf_validator valid; 00335 xmlNodePtr map_root; 00336 xmlNsPtr map_ns; 00337 00338 g_return_val_if_fail((path != NULL), FALSE); 00339 if (path == NULL) 00340 { 00341 return FALSE; 00342 } 00343 doc = xmlParseFile(path); 00344 if (doc == NULL) 00345 { 00346 return FALSE; 00347 } 00348 if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_MAP_SCHEMA, doc)) 00349 { 00350 xmlFreeDoc(doc); 00351 return FALSE; 00352 } 00353 map_root = xmlDocGetRootElement(doc); 00354 map_ns = map_root->ns; 00355 iter.ns = map_ns; 00356 valid.error_state = ERR_BACKEND_NO_ERR; 00357 valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal); 00358 qsf_valid_foreach(map_root, qsf_map_validation_handler, &iter, &valid); 00359 if (valid.error_state != ERR_BACKEND_NO_ERR) 00360 { 00361 g_hash_table_destroy(valid.validation_table); 00362 return FALSE; 00363 } 00364 g_hash_table_destroy(valid.validation_table); 00365 return TRUE; 00366 }
| gboolean is_qsf_map_be | ( | qsf_param * | params | ) |
Validate a QSF map file.
| params | Pointer to qsf_param context |
Definition at line 285 of file qsf-xml-map.c.
00286 { 00287 xmlDocPtr doc; 00288 struct qsf_node_iterate iter; 00289 qsf_validator valid; 00290 xmlNodePtr map_root; 00291 xmlNsPtr map_ns; 00292 gchar *path; 00293 00294 g_return_val_if_fail((params != NULL), FALSE); 00295 qof_backend_get_error(params->be); 00296 path = g_strdup(params->filepath); 00297 if (path == NULL) 00298 { 00299 qof_backend_set_error(params->be, ERR_FILEIO_FILE_NOT_FOUND); 00300 return FALSE; 00301 } 00302 doc = xmlParseFile(path); 00303 if (doc == NULL) 00304 { 00305 qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR); 00306 return FALSE; 00307 } 00308 if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_MAP_SCHEMA, doc)) 00309 { 00310 qof_backend_set_error(params->be, ERR_QSF_INVALID_MAP); 00311 return FALSE; 00312 } 00313 map_root = xmlDocGetRootElement(doc); 00314 map_ns = map_root->ns; 00315 iter.ns = map_ns; 00316 valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal); 00317 valid.error_state = ERR_BACKEND_NO_ERR; 00318 qsf_valid_foreach(map_root, qsf_map_validation_handler, &iter, &valid); 00319 if (valid.error_state != ERR_BACKEND_NO_ERR) 00320 { 00321 qof_backend_set_error(params->be, valid.error_state); 00322 g_hash_table_destroy(valid.validation_table); 00323 return FALSE; 00324 } 00325 qof_backend_get_error(params->be); 00326 g_hash_table_destroy(valid.validation_table); 00327 return TRUE; 00328 }
| gboolean is_qsf_object | ( | const gchar * | path | ) |
Validate a QSF file and identify a suitable QSF map.
| path | Absolute or relative path to the file to be validated. |
The file is validated against the QSF object schema, qsf-object.xsd.xml and each object described in the file is checked to find out if a suitable QSF map exists. Map files are accepted if all objects described in the QSF object file are defined in the QSF map.
Definition at line 192 of file qsf-xml.c.
00193 { 00194 xmlDocPtr doc; 00195 00196 g_return_val_if_fail((path != NULL), FALSE); 00197 if (path == NULL) 00198 { 00199 return FALSE; 00200 } 00201 doc = xmlParseFile(path); 00202 if (doc == NULL) 00203 { 00204 return FALSE; 00205 } 00206 if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc)) 00207 { 00208 xmlFreeDoc(doc); 00209 return FALSE; 00210 } 00211 xmlFreeDoc(doc); 00212 /* Note cannot test against a map here, so if the file is valid QSF, 00213 accept it and work out the details later. */ 00214 return TRUE; 00215 }
| gboolean is_qsf_object_be | ( | qsf_param * | params | ) |
Validate a QSF file and identify a suitable QSF map.
| params | Pointer to qsf_param context |
The file is validated against the QSF object schema, qsf-object.xsd.xml and each object described in the file is checked to find out if a suitable QSF map exists. Map files are accepted if all objects described in the QSF object file are defined in the QSF map.
Definition at line 266 of file qsf-xml.c.
00267 { 00268 gboolean result; 00269 xmlDocPtr doc; 00270 GList *maps; 00271 gchar *path; 00272 00273 g_return_val_if_fail((params != NULL), FALSE); 00274 path = g_strdup(params->filepath); 00275 if (path == NULL) 00276 { 00277 qof_backend_set_error(params->be, ERR_FILEIO_FILE_NOT_FOUND); 00278 return FALSE; 00279 } 00280 /* skip validation if is_our_qsf_object has already been called. */ 00281 if (ERR_QSF_INVALID_OBJ == qof_backend_get_error(params->be)) 00282 { 00283 return FALSE; 00284 } 00285 if (params->file_type == QSF_UNDEF) 00286 { 00287 doc = xmlParseFile(path); 00288 if (doc == NULL) 00289 { 00290 qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR); 00291 return FALSE; 00292 } 00293 if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc)) 00294 { 00295 qof_backend_set_error(params->be, ERR_QSF_INVALID_OBJ); 00296 return FALSE; 00297 } 00298 } 00299 result = FALSE; 00300 /* retrieve list of maps from config frame. */ 00301 for (maps = params->map_files; maps; maps = maps->next) 00302 { 00303 QofBackendError err; 00304 result = is_qsf_object_with_map_be(maps->data, params); 00305 err = qof_backend_get_error(params->be); 00306 if ((err == ERR_BACKEND_NO_ERR) && result) 00307 { 00308 params->map_path = maps->data; 00309 PINFO ("map chosen = %s", params->map_path); 00310 break; 00311 } 00312 /* pop the error back on the stack. */ 00313 else 00314 { 00315 qof_backend_set_error(params->be, err); 00316 } 00317 } 00318 return result; 00319 }
| gboolean is_qsf_object_with_map | ( | const gchar * | path, | |
| gchar * | map_file | |||
| ) |
Validate a QSF file and a selected QSF map.
| path | Absolute or relative path to the selected QSF map file | |
| map_file | Name of the map file |
Definition at line 221 of file qsf-xml-map.c.
00222 { 00223 xmlDocPtr doc, map_doc; 00224 gint valid_count; 00225 struct qsf_node_iterate iter; 00226 xmlNodePtr map_root, object_root; 00227 xmlNsPtr map_ns; 00228 qsf_validator valid; 00229 gchar *map_path; 00230 00231 map_path = g_strdup_printf("%s/%s", QSF_SCHEMA_DIR, map_file); 00232 if (path == NULL) 00233 { 00234 return FALSE; 00235 } 00236 doc = xmlParseFile(path); 00237 if (doc == NULL) 00238 { 00239 return FALSE; 00240 } 00241 if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc)) 00242 { 00243 return FALSE; 00244 } 00245 object_root = xmlDocGetRootElement(doc); 00246 if (map_path == NULL) 00247 { 00248 return FALSE; 00249 } 00250 valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal); 00251 map_doc = xmlParseFile(map_path); 00252 if (map_doc == NULL) 00253 { 00254 return FALSE; 00255 } 00256 if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_MAP_SCHEMA, map_doc)) 00257 { 00258 return FALSE; 00259 } 00260 map_root = xmlDocGetRootElement(map_doc); 00261 valid.map_calculated_count = 0; 00262 valid.valid_object_count = 0; 00263 valid.error_state = ERR_BACKEND_NO_ERR; 00264 map_ns = map_root->ns; 00265 iter.ns = map_ns; 00266 qsf_valid_foreach(map_root, qsf_map_validation_handler, &iter, &valid); 00267 iter.ns = object_root->ns; 00268 qsf_valid_foreach(object_root, qsf_object_validation_handler, &iter, &valid); 00269 if (valid.error_state != ERR_BACKEND_NO_ERR) 00270 { 00271 g_hash_table_destroy(valid.validation_table); 00272 return FALSE; 00273 } 00274 valid_count = 0 - g_hash_table_size(valid.validation_table); 00275 valid_count += valid.map_calculated_count; 00276 valid_count += valid.valid_object_count; 00277 g_hash_table_destroy(valid.validation_table); 00278 if (valid_count == 0) 00279 { 00280 return TRUE; 00281 } 00282 return FALSE; 00283 }
| gboolean is_qsf_object_with_map_be | ( | gchar * | map_path, | |
| qsf_param * | params | |||
| ) |
Validate a QSF file and a selected QSF map.
| map_path | Absolute or relative path to the selected QSF map file | |
| params | Pointer to qsf_param context |
This backend twin also sets QofBackendError codes.
Definition at line 124 of file qsf-xml-map.c.
00125 { 00126 xmlDocPtr doc, map_doc; 00127 gint valid_count, calc_count; 00128 struct qsf_node_iterate iter; 00129 xmlNodePtr map_root, object_root; 00130 xmlNsPtr map_ns; 00131 qsf_validator valid; 00132 gchar *path; 00133 gchar *map_path; 00134 00135 g_return_val_if_fail((params != NULL), FALSE); 00136 PINFO (" mapfile=%s", map_file); 00137 path = g_strdup(params->filepath); 00138 map_path = g_strdup_printf("%s/%s", QSF_SCHEMA_DIR, map_file); 00139 if (path == NULL) 00140 { 00141 qof_backend_set_error(params->be, ERR_FILEIO_FILE_NOT_FOUND); 00142 return FALSE; 00143 } 00144 doc = xmlParseFile(path); 00145 if (doc == NULL) 00146 { 00147 qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR); 00148 return FALSE; 00149 } 00150 if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_OBJECT_SCHEMA, doc)) 00151 { 00152 qof_backend_set_error(params->be, ERR_QSF_INVALID_OBJ); 00153 return FALSE; 00154 } 00155 object_root = xmlDocGetRootElement(doc); 00156 if (map_path == NULL) 00157 { 00158 qof_backend_set_error(params->be, ERR_FILEIO_FILE_NOT_FOUND); 00159 return FALSE; 00160 } 00161 valid.validation_table = g_hash_table_new(g_str_hash, g_str_equal); 00162 map_doc = xmlParseFile(map_path); 00163 if (map_doc == NULL) 00164 { 00165 qof_backend_set_error(params->be, ERR_FILEIO_PARSE_ERROR); 00166 return FALSE; 00167 } 00168 if (TRUE != qsf_is_valid(QSF_SCHEMA_DIR, QSF_MAP_SCHEMA, map_doc)) 00169 { 00170 qof_backend_set_error(params->be, ERR_QSF_INVALID_MAP); 00171 return FALSE; 00172 } 00173 map_root = xmlDocGetRootElement(map_doc); 00174 valid.map_calculated_count = 0; 00175 valid.valid_object_count = 0; 00176 valid.qof_registered_count = 0; 00177 valid.error_state = ERR_BACKEND_NO_ERR; 00178 map_ns = map_root->ns; 00179 iter.ns = object_root->ns; 00180 qsf_valid_foreach(object_root, qsf_object_validation_handler, &iter, &valid); 00181 iter.ns = map_ns; 00182 qsf_valid_foreach(map_root, qsf_map_validation_handler, &iter, &valid); 00183 if (valid.error_state != ERR_BACKEND_NO_ERR) 00184 { 00185 qof_backend_set_error(params->be, valid.error_state); 00186 g_hash_table_destroy(valid.validation_table); 00187 return FALSE; 00188 } 00189 /* check all counted objects are valid */ 00190 /* Should be: 00191 the same number of valid object calculations as there are defined in the map. 00192 And the number of calculations must match the number of unregistered 00193 objects plus the number of registered objects defined. */ 00194 valid_count = g_hash_table_size(valid.validation_table) - valid.map_calculated_count; 00195 calc_count = valid.map_calculated_count - 00196 (valid.valid_object_count + valid.qof_registered_count); 00197 if (valid_count == 0 && calc_count == 0) 00198 { 00199 g_hash_table_destroy(valid.validation_table); 00200 qof_backend_get_error(params->be); 00201 return TRUE; 00202 } 00203 qof_backend_set_error(params->be, ERR_QSF_WRONG_MAP); 00204 /* the object is OK, only the map is wrong. */ 00205 PINFO (" Map is wrong. map:%d object:%d reg:%d size:%d result:%d", 00206 valid.map_calculated_count, valid.valid_object_count, 00207 valid.qof_registered_count, 00208 g_hash_table_size(valid.validation_table), valid_count); 00209 if (valid_count != 0) 00210 { 00211 PINFO (" size - map != 0. actual: %d.", valid_count); 00212 } 00213 if (calc_count != 0) 00214 { 00215 PINFO (" map - (object + registered) != 0. Actual: %d.", calc_count); 00216 } 00217 g_hash_table_destroy(valid.validation_table); 00218 return TRUE; 00219 }
| void qsf_book_node_handler | ( | xmlNodePtr | child, | |
| xmlNsPtr | qsf_ns, | |||
| qsf_param * | params | |||
| ) |
Book and book-guid node handler.
Reads the book count="" attribute (currently only 1 QofBook is supported per QSF object file) Sets the book-guid as the GncGUID of the current QofBackend QofBook in qsf_param. Calls the next handler, qsf_object_node_handler, with the child of the book tag.
Definition at line 377 of file qsf-xml.c.
00378 { 00379 gchar *book_count_s, *tail; 00380 gint book_count; 00381 xmlNodePtr child_node; 00382 struct qsf_node_iterate iter; 00383 gchar *buffer; 00384 GncGUID book_guid; 00385 00386 g_return_if_fail(child); 00387 g_return_if_fail(params); 00388 ENTER (" child=%s", child->name); 00389 if (qsf_is_element(child, ns, QSF_BOOK_TAG)) 00390 { 00391 book_count_s = (gchar*)xmlGetProp(child, BAD_CAST QSF_BOOK_COUNT); 00392 if (book_count_s) 00393 { 00394 book_count = (gint)strtol(book_count_s, &tail, 0); 00395 /* More than one book not currently supported. */ 00396 g_return_if_fail(book_count == 1); 00397 } 00398 iter.ns = ns; 00399 child_node = child->children->next; 00400 if (qsf_is_element(child_node, ns, QSF_BOOK_GUID)) 00401 { 00402 DEBUG (" trying to set book GncGUID"); 00403 buffer = g_strdup((gchar*)xmlNodeGetContent(child_node)); 00404 g_return_if_fail(TRUE == string_to_guid(buffer, &book_guid)); 00405 qof_instance_set_guid(QOF_INSTANCE(params->book), &book_guid); 00406 xmlNewChild(params->output_node, params->qsf_ns, 00407 BAD_CAST QSF_BOOK_GUID, BAD_CAST buffer); 00408 g_free(buffer); 00409 } 00410 qsf_node_foreach(child, qsf_object_node_handler, &iter, params); 00411 } 00412 LEAVE (" "); 00413 }
| gint qsf_check_tag | ( | qsf_param * | params, | |
| gchar * | qof_type | |||
| ) |
shorthand function
This may look repetitive but each one is used separately as well as in a block.
Definition at line 69 of file qsf-xml.c.
00070 { 00071 return qsf_is_element(params->child_node, params->qsf_ns, qof_type); 00072 }
| gint qsf_compare_tag_strings | ( | const xmlChar * | node_name, | |
| gchar * | tag_name | |||
| ) |
| gint qsf_is_element | ( | xmlNodePtr | a, | |
| xmlNsPtr | ns, | |||
| gchar * | c | |||
| ) |
shorthand function
This may look repetitive but each one is used separately as well as in a block.
Definition at line 55 of file qsf-xml.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 }
| gboolean qsf_is_valid | ( | const gchar * | schema_dir, | |
| const gchar * | schema_filename, | |||
| xmlDocPtr | doc | |||
| ) |
Compares an xmlDoc in memory against the schema file.
| schema_dir | set at compile time to $prefix/share/qsf/ | |
| schema_filename | Either the QSF Object Schema or the QSF Map Schema. | |
| doc | The xmlDoc read from the file using libxml2. |
Incorrect validation will result in output to the terminal window.
Definition at line 75 of file qsf-xml.c.
00076 { 00077 xmlSchemaParserCtxtPtr qsf_schema_file; 00078 xmlSchemaPtr qsf_schema; 00079 xmlSchemaValidCtxtPtr qsf_context; 00080 gchar *schema_path; 00081 gint result; 00082 00083 g_return_val_if_fail(doc || schema_filename, FALSE); 00084 schema_path = g_strdup_printf("%s/%s", schema_dir, schema_filename); 00085 qsf_schema_file = xmlSchemaNewParserCtxt(schema_path); 00086 qsf_schema = xmlSchemaParse(qsf_schema_file); 00087 qsf_context = xmlSchemaNewValidCtxt(qsf_schema); 00088 result = xmlSchemaValidateDoc(qsf_context, doc); 00089 xmlSchemaFreeParserCtxt(qsf_schema_file); 00090 xmlSchemaFreeValidCtxt(qsf_context); 00091 xmlSchemaFree(qsf_schema); 00092 g_free(schema_path); 00093 if (result == 0) 00094 { 00095 return TRUE; 00096 } 00097 return FALSE; 00098 }
| GList** qsf_map_prepare_list | ( | GList ** | maps | ) |
Prepare the default list of maps.
Prepend the default maps to the supplied GList.
The GList remains the property of the caller.
Definition at line 143 of file qsf-backend.c.
00144 { 00145 *maps = g_list_prepend(*maps, "pilot-qsf-GnuCashInvoice.xml"); 00146 *maps = g_list_prepend(*maps, "pilot-qsf-gncCustomer.xml"); 00147 return maps; 00148 }
| void qsf_node_foreach | ( | xmlNodePtr | parent, | |
| qsf_nodeCB | cb, | |||
| struct qsf_node_iterate * | iter, | |||
| qsf_param * | params | |||
| ) |
Iterate over the children of the parent node.
Only iterates over the immediate children of the parent - this function is not recursive.
Definition at line 114 of file qsf-xml.c.
00116 { 00117 xmlNodePtr cur_node; 00118 00119 g_return_if_fail(iter->ns); 00120 iter->fcn = &cb; 00121 for (cur_node = parent->children; cur_node != NULL; cur_node = cur_node->next) 00122 { 00123 cb(cur_node, iter->ns, params); 00124 } 00125 }
| xmlDocPtr qsf_object_convert | ( | xmlDocPtr | mapDoc, | |
| xmlNodePtr | qsf_root, | |||
| qsf_param * | params | |||
| ) |
Convert between QSF objects.
This is the main workhorse of the conversion between QSF objects using maps.
| mapDoc | The map document, parsed by libxml2. | |
| qsf_root | The top node of the QSF object to be converted using the map. | |
| params | The QSF backend parameters. |
Definition at line 838 of file qsf-xml-map.c.
00839 { 00840 /* mapDoc : map document. qsf_root: incoming QSF root node. */ 00841 struct qsf_node_iterate iter; 00842 xmlDocPtr output_doc; 00843 xmlNode *cur_node; 00844 xmlNode *map_root, *output_root; 00845 00846 g_return_val_if_fail((mapDoc && qsf_root && params), NULL); 00847 ENTER (" root=%s", qsf_root->name); 00848 /* prepare the intermediary document */ 00849 iter.ns = params->qsf_ns; 00850 output_doc = xmlNewDoc(BAD_CAST QSF_XML_VERSION); 00851 output_root = xmlNewNode(NULL, BAD_CAST QSF_ROOT_TAG); 00852 xmlDocSetRootElement(output_doc, output_root); 00853 xmlSetNs(output_root, params->qsf_ns); 00854 params->output_node = xmlNewChild(output_root, params->qsf_ns, 00855 BAD_CAST QSF_BOOK_TAG, NULL); 00856 xmlNewProp(params->output_node, BAD_CAST QSF_BOOK_COUNT, BAD_CAST "1"); 00857 /* parse the incoming QSF */ 00858 qsf_book_node_handler(qsf_root->children->next, params->qsf_ns, params); 00859 /* parse the map and calculate the values */ 00860 map_root = xmlDocGetRootElement(mapDoc); 00861 params->foreach_limit = 0; 00862 iter.ns = params->map_ns; 00863 qsf_node_foreach(map_root, qsf_map_top_node_handler, &iter, params); 00864 /* identify the entities of iterator type. */ 00865 iter.ns = params->qsf_ns; 00866 qsf_node_foreach(qsf_root->children->next, iterator_cb, &iter, params); 00867 00868 params->count = 0; 00869 for (cur_node = map_root->children; cur_node != NULL; cur_node = cur_node->next) 00870 { 00871 params->convert_node = cur_node; 00872 if (qsf_is_element(cur_node, params->map_ns, MAP_OBJECT_TAG)) 00873 { 00874 gint i; 00875 00876 params->lister = NULL; 00877 /* cur_node describes the target object */ 00878 if (!qof_class_is_registered( 00879 (QofIdTypeConst)xmlGetProp(cur_node, 00880 BAD_CAST MAP_TYPE_ATTR))) 00881 { 00882 continue; 00883 } 00884 qsf_add_object_tag(params, params->count); 00885 params->count++; 00886 iter.ns = params->map_ns; 00887 for (i = 0; i < params->foreach_limit; i++) 00888 { 00889 qsf_node_foreach(cur_node, qsf_map_object_handler, &iter, params); 00890 params->qsf_object_list = g_list_next(params->qsf_object_list); 00891 qsf_add_object_tag(params, params->count); 00892 params->count++; 00893 } 00894 } 00895 } 00896 params->file_type = OUR_QSF_OBJ; 00897 /* use for debugging */ 00898 /* xmlSaveFormatFileEnc("-", output_doc, "UTF-8", 1);*/ 00899 LEAVE (" "); 00900 return output_doc; 00901 }
| void qsf_object_node_handler | ( | xmlNodePtr | child, | |
| xmlNsPtr | qsf_ns, | |||
| qsf_param * | params | |||
| ) |
Despite the name, this function handles the QSF object book tag AND the object tags.
Used to parse object and map files.
Definition at line 344 of file qsf-xml.c.
00345 { 00346 struct qsf_node_iterate iter; 00347 qsf_objects *object_set; 00348 gchar *tail, *object_count_s; 00349 gint64 c; 00350 00351 g_return_if_fail(child != NULL); 00352 g_return_if_fail(qsf_ns != NULL); 00353 params->qsf_ns = qsf_ns; 00354 if (qsf_is_element(child, qsf_ns, QSF_OBJECT_TAG)) 00355 { 00356 params->qsf_parameter_hash = NULL; 00357 c = 0; 00358 object_set = g_new(qsf_objects, 1); 00359 params->object_set = object_set; 00360 object_set->object_count = 0; 00361 object_set->parameters = g_hash_table_new(g_str_hash, g_str_equal); 00362 object_set->object_type = g_strdup((gchar*)xmlGetProp(child, 00363 BAD_CAST QSF_OBJECT_TYPE)); 00364 object_count_s = g_strdup((gchar*)xmlGetProp(child, 00365 BAD_CAST QSF_OBJECT_COUNT)); 00366 c = (gint64)strtol(object_count_s, &tail, 0); 00367 object_set->object_count = (gint)c; 00368 g_free(object_count_s); 00369 params->qsf_object_list = g_list_prepend(params->qsf_object_list, object_set); 00370 iter.ns = qsf_ns; 00371 params->qsf_parameter_hash = object_set->parameters; 00372 qsf_node_foreach(child, qsf_parameter_handler, &iter, params); 00373 } 00374 }
| void qsf_object_validation_handler | ( | xmlNodePtr | child, | |
| xmlNsPtr | ns, | |||
| qsf_validator * | valid | |||
| ) |
Checks all incoming objects for QOF registration.
Sums all existing objects in the QSF and counts the number of those objects that are also registered with QOF in the host application.
Definition at line 128 of file qsf-xml.c.
00129 { 00130 xmlNodePtr cur_node; 00131 xmlChar *object_declaration; 00132 guint count; 00133 00134 count = 0; 00135 for (cur_node = child->children; cur_node != NULL; 00136 cur_node = cur_node->next) 00137 { 00138 if (qsf_is_element(cur_node, ns, QSF_OBJECT_TAG)) 00139 { 00140 object_declaration = xmlGetProp(cur_node, BAD_CAST QSF_OBJECT_TYPE); 00141 count = g_hash_table_size(valid->validation_table); 00142 g_hash_table_insert(valid->validation_table, object_declaration, xmlNodeGetContent(cur_node)); 00143 if (g_hash_table_size(valid->validation_table) > count) 00144 { 00145 valid->valid_object_count++; 00146 if (TRUE == qof_class_is_registered((QofIdTypeConst) object_declaration)) 00147 { 00148 valid->qof_registered_count++; 00149 } 00150 } 00151 } 00152 } 00153 }
| gint qsf_strings_equal | ( | const xmlChar * | node_name, | |
| gchar * | tag_name | |||
| ) |
| void qsf_valid_foreach | ( | xmlNodePtr | parent, | |
| qsf_validCB | cb, | |||
| struct qsf_node_iterate * | iter, | |||
| qsf_validator * | valid | |||
| ) |
Validate the children of the parent node.
Definition at line 101 of file qsf-xml.c.
00103 { 00104 xmlNodePtr cur_node; 00105 00106 iter->v_fcn = &cb; 00107 for (cur_node = parent->children; cur_node != NULL; cur_node = cur_node->next) 00108 { 00109 cb(cur_node, iter->ns, valid); 00110 } 00111 }
| KvpValue* string_to_kvp_value | ( | const gchar * | content, | |
| KvpValueType | type | |||
| ) |
Convert a string value into KvpValue.
Partner to kvp_value_to_string. Given the type of KvpValue required, attempts to convert the string into that type of value.
| content | A string representation of the value, ideally as output by kvp_value_to_string. | |
| type | KvpValueType of the intended KvpValue |
Definition at line 1155 of file qsf-backend.c.
01156 { 01157 gchar *tail; 01158 gint64 cm_i64; 01159 double cm_double; 01160 gnc_numeric cm_numeric; 01161 GncGUID *cm_guid; 01162 struct tm kvp_time; 01163 time_t kvp_time_t; 01164 Timespec cm_date; 01165 01166 switch (type) 01167 { 01168 case KVP_TYPE_GINT64: 01169 errno = 0; 01170 cm_i64 = strtoll(content, &tail, 0); 01171 if (errno == 0) 01172 { 01173 return kvp_value_new_gint64(cm_i64); 01174 } 01175 break; 01176 case KVP_TYPE_DOUBLE: 01177 errno = 0; 01178 cm_double = strtod(content, &tail); 01179 if (errno == 0) 01180 { 01181 return kvp_value_new_double(cm_double); 01182 } 01183 break; 01184 case KVP_TYPE_NUMERIC: 01185 string_to_gnc_numeric(content, &cm_numeric); 01186 return kvp_value_new_gnc_numeric(cm_numeric); 01187 break; 01188 case KVP_TYPE_STRING: 01189 return kvp_value_new_string(content); 01190 break; 01191 case KVP_TYPE_GUID: 01192 cm_guid = g_new(GncGUID, 1); 01193 if (TRUE == string_to_guid(content, cm_guid)) 01194 { 01195 return kvp_value_new_guid(cm_guid); 01196 } 01197 break; 01198 case KVP_TYPE_TIMESPEC: 01199 strptime(content, QSF_XSD_TIME, &kvp_time); 01200 kvp_time_t = mktime(&kvp_time); 01201 timespecFromTime_t(&cm_date, kvp_time_t); 01202 return kvp_value_new_timespec(cm_date); 01203 break; 01204 case KVP_TYPE_BINARY: 01205 // return kvp_value_new_binary(value->value.binary.data, 01206 // value->value.binary.datasize); 01207 break; 01208 case KVP_TYPE_GLIST: 01209 // return kvp_value_new_glist(value->value.list); 01210 break; 01211 case KVP_TYPE_FRAME: 01212 // return kvp_value_new_frame(value->value.frame); 01213 break; 01214 case KVP_TYPE_GDATE: 01215 { 01216 GDate date; 01217 g_date_clear(&date, 1); 01218 g_date_set_parse(&date, content); 01219 return kvp_value_new_gdate(date); 01220 } 01221 } 01222 return NULL; 01223 }
1.5.7.1