1 #include "openils/oils_idl.h"
8 #include <libxml/globals.h>
9 #include <libxml/xmlerror.h>
10 #include <libxml/parser.h>
11 #include <libxml/tree.h>
12 #include <libxml/debugXML.h>
13 #include <libxml/xmlmemory.h>
15 #define PERSIST_NS "http://open-ils.org/spec/opensrf/IDL/persistence/v1"
16 #define OBJECT_NS "http://open-ils.org/spec/opensrf/IDL/objects/v1"
17 #define BASE_NS "http://opensrf.org/spec/IDL/base/v1"
18 #define REPORTER_NS "http://open-ils.org/spec/opensrf/IDL/reporter/v1"
19 #define PERM_NS "http://open-ils.org/spec/opensrf/IDL/permacrud/v1"
21 static xmlDocPtr idlDoc = NULL; // parse and store the IDL here
23 /* parse and store the IDL here */
24 static osrfHash* idlHash;
26 static void add_std_fld( osrfHash* fields_hash, const char* field_name, unsigned pos );
27 osrfHash* oilsIDL(void) { return idlHash; }
28 osrfHash* oilsIDLInit( const char* idl_filename ) {
30 if (idlHash) return idlHash;
32 char* prop_str = NULL;
34 idlHash = osrfNewHash();
35 osrfHash* class_def_hash = NULL;
37 osrfLogInfo(OSRF_LOG_MARK, "Parsing the IDL XML...");
38 idlDoc = xmlReadFile( idl_filename, NULL, XML_PARSE_XINCLUDE );
41 osrfLogError(OSRF_LOG_MARK, "Could not load or parse the IDL XML file!");
45 osrfLogDebug(OSRF_LOG_MARK, "Initializing the Fieldmapper IDL...");
47 xmlNodePtr docRoot = xmlDocGetRootElement(idlDoc);
48 xmlNodePtr kid = docRoot->children;
50 if (!strcmp( (char*)kid->name, "class" )) {
52 class_def_hash = osrfNewHash();
53 char* current_class_name = (char*) xmlGetProp(kid, BAD_CAST "id");
55 osrfHashSet( class_def_hash, current_class_name, "classname" );
56 osrfHashSet( class_def_hash, xmlGetNsProp(kid, BAD_CAST "fieldmapper", BAD_CAST OBJECT_NS), "fieldmapper" );
57 osrfHashSet( class_def_hash, xmlGetNsProp(kid, BAD_CAST "readonly", BAD_CAST PERSIST_NS), "readonly" );
59 osrfHashSet( idlHash, class_def_hash, current_class_name );
61 if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "tablename", BAD_CAST PERSIST_NS))) {
62 osrfLogDebug(OSRF_LOG_MARK, "Using table '%s' for class %s", prop_str, current_class_name );
70 if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "restrict_primary", BAD_CAST PERSIST_NS))) {
71 osrfLogDebug(OSRF_LOG_MARK, "Delete restriction policy set at '%s' for pkey of class %s", prop_str, current_class_name );
79 if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "virtual", BAD_CAST PERSIST_NS))) {
87 // Tokenize controller attribute into an osrfStringArray
88 prop_str = (char*) xmlGetProp(kid, BAD_CAST "controller");
90 osrfLogDebug(OSRF_LOG_MARK, "Controller list is %s", prop_str );
91 osrfStringArray* controller = osrfStringArrayTokenize( prop_str, ' ' );
93 osrfHashSet( class_def_hash, controller, "controller");
95 osrfHash* current_links_hash = osrfNewHash();
96 osrfHash* current_fields_hash = osrfNewHash();
98 osrfHashSet( class_def_hash, current_fields_hash, "fields" );
99 osrfHashSet( class_def_hash, current_links_hash, "links" );
101 xmlNodePtr _cur = kid->children;
105 if (!strcmp( (char*)_cur->name, "fields" )) {
107 if( (prop_str = (char*)xmlGetNsProp(_cur, BAD_CAST "primary", BAD_CAST PERSIST_NS)) ) {
115 if( (prop_str = (char*)xmlGetNsProp(_cur, BAD_CAST "sequence", BAD_CAST PERSIST_NS)) ) {
123 unsigned int array_pos = 0;
124 char array_pos_buf[ 7 ]; // For up to 1,000,000 fields per class
126 xmlNodePtr _f = _cur->children;
128 if (strcmp( (char*)_f->name, "field" )) {
133 // Get the field name. If it's one of the three standard
134 // fields that we always generate, ignore it.
135 char* field_name = (char*)xmlGetProp(_f, BAD_CAST "name");
137 osrfLogDebug(OSRF_LOG_MARK,
138 "Found field %s for class %s", field_name, current_class_name );
139 if( !strcmp( field_name, "isnew" )
140 || !strcmp( field_name, "ischanged" )
141 || !strcmp( field_name, "isdeleted" ) ) {
147 osrfLogDebug(OSRF_LOG_MARK,
148 "Found field with no name for class %s", current_class_name );
153 osrfHash* field_def_hash = osrfNewHash();
155 // Insert array_position
156 snprintf( array_pos_buf, sizeof( array_pos_buf ), "%u", array_pos++ );
157 osrfHashSet( field_def_hash, strdup( array_pos_buf ), "array_position" );
159 // Tokenize suppress_controller attribute into an osrfStringArray
160 if( (prop_str = (char*)xmlGetProp(_f, BAD_CAST "suppress_controller")) ) {
161 osrfLogDebug(OSRF_LOG_MARK, "Controller suppression list is %s", prop_str );
162 osrfStringArray* controller = osrfStringArrayTokenize( prop_str, ' ' );
163 osrfHashSet( field_def_hash, controller, "suppress_controller");
166 if( (prop_str = (char*)xmlGetNsProp(_f, BAD_CAST "i18n", BAD_CAST PERSIST_NS)) ) {
174 if( (prop_str = (char*)xmlGetNsProp(_f, BAD_CAST "virtual", BAD_CAST PERSIST_NS)) ) {
180 } else { // default to virtual
188 if( (prop_str = (char*)xmlGetNsProp(_f, BAD_CAST "primitive", BAD_CAST PERSIST_NS)) ) {
196 osrfHashSet( field_def_hash, field_name, "name" );
205 // Create three standard, stereotyped virtual fields for every class
206 add_std_fld( current_fields_hash, "isnew", array_pos++ );
207 add_std_fld( current_fields_hash, "ischanged", array_pos++ );
208 add_std_fld( current_fields_hash, "isdeleted", array_pos );
212 if (!strcmp( (char*)_cur->name, "links" )) {
213 xmlNodePtr _l = _cur->children;
216 if (strcmp( (char*)_l->name, "link" )) {
221 osrfHash* link_def_hash = osrfNewHash();
223 if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "reltype")) ) {
229 osrfLogDebug(OSRF_LOG_MARK, "Adding link with reltype %s", prop_str );
231 osrfLogDebug(OSRF_LOG_MARK, "Adding link with no reltype" );
233 if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "key")) ) {
239 osrfLogDebug(OSRF_LOG_MARK, "Link fkey is %s", prop_str );
241 osrfLogDebug(OSRF_LOG_MARK, "Link with no fkey" );
243 if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "class")) ) {
249 osrfLogDebug(OSRF_LOG_MARK, "Link fclass is %s", prop_str );
251 osrfLogDebug(OSRF_LOG_MARK, "Link with no fclass" );
253 // Tokenize map attribute into an osrfStringArray
254 prop_str = (char*) xmlGetProp(_l, BAD_CAST "map");
256 osrfLogDebug(OSRF_LOG_MARK, "Link mapping list is %s", prop_str );
257 osrfStringArray* map = osrfStringArrayTokenize( prop_str, ' ' );
258 osrfHashSet( link_def_hash, map, "map");
261 if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "field")) ) {
267 osrfLogDebug(OSRF_LOG_MARK, "Link fclass is %s", prop_str );
269 osrfLogDebug(OSRF_LOG_MARK, "Link with no fclass" );
280 /**** Structure of permacrud in memory ****
283 { permission : [ x, y, z ],
284 global_required : "true", -- anything else, or missing, is false
285 ignore_object_perms : "true", -- anything else, or missing, is false
286 local_context : [ f1, f2 ],
287 foreign_context : { class1 : { fkey : local_class_key, field : class1_field, context : [ a, b, c ] }, ...}
289 retrieve : null, -- no perm check, or structure similar to the others
290 update : -- like create
292 delete : -- like create
296 **** Structure of permacrud in memory ****/
298 if (!strcmp( (char*)_cur->name, "permacrud" )) {
299 osrfHash* pcrud = osrfNewHash();
300 osrfHashSet( class_def_hash, pcrud, "permacrud" );
301 xmlNodePtr _l = _cur->children;
302 char * ignore_object_perms = (char*) xmlGetProp(_cur, BAD_CAST "ignore_object_perms");
305 if (strcmp( (char*)_l->name, "actions" )) {
310 xmlNodePtr _a = _l->children;
313 const char* action_name = (const char*) _a->name;
315 strcmp( action_name, "create" ) &&
316 strcmp( action_name, "retrieve" ) &&
317 strcmp( action_name, "update" ) &&
318 strcmp( action_name, "delete" )
324 osrfLogDebug(OSRF_LOG_MARK, "Found Permacrud action %s for class %s",
325 action_name, current_class_name );
327 osrfHash* action_def_hash = osrfNewHash();
328 osrfHashSet( pcrud, action_def_hash, action_name );
330 // Set the class-wide ignore_object_perms flag
331 osrfHashSet( action_def_hash, ignore_object_perms, "ignore_object_perms");
333 // Tokenize permission attribute into an osrfStringArray
334 prop_str = (char*) xmlGetProp(_a, BAD_CAST "permission");
336 osrfLogDebug(OSRF_LOG_MARK,
337 "Permacrud permission list is %s", prop_str );
338 osrfStringArray* map = osrfStringArrayTokenize( prop_str, ' ' );
339 osrfHashSet( action_def_hash, map, "permission");
342 osrfHashSet( action_def_hash,
343 (char*)xmlGetNoNsProp(_a, BAD_CAST "owning_user"), "owning_user");
345 osrfHashSet( action_def_hash,
346 (char*)xmlGetNoNsProp(_a, BAD_CAST "global_required"), "global_required");
348 // Tokenize context_field attribute into an osrfStringArray
349 prop_str = (char*) xmlGetProp(_a, BAD_CAST "context_field");
351 osrfLogDebug(OSRF_LOG_MARK,
352 "Permacrud context_field list is %s", prop_str );
353 map = osrfStringArrayTokenize( prop_str, ' ' );
354 osrfHashSet( action_def_hash, map, "local_context");
357 osrfHash* foreign_context = osrfNewHash();
358 osrfHashSet( action_def_hash, foreign_context, "foreign_context");
360 xmlNodePtr _f = _a->children;
363 if ( strcmp( (char*)_f->name, "context" ) ) {
368 if( (prop_str = (char*)xmlGetNoNsProp(_f, BAD_CAST "link")) ) {
369 osrfLogDebug(OSRF_LOG_MARK,
370 "Permacrud context link definition is %s", prop_str );
372 osrfHash* _tmp_fcontext = osrfNewHash();
374 // Store pointers to elements already stored
375 // from the <link> aggregate
376 osrfHash* _flink = osrfHashGet( current_links_hash, prop_str );
377 osrfHashSet( _tmp_fcontext, osrfHashGet(_flink, "field"), "fkey" );
378 osrfHashSet( _tmp_fcontext, osrfHashGet(_flink, "key"), "field" );
381 if( (prop_str = (char*)xmlGetNoNsProp(_f, BAD_CAST "jump")) )
382 osrfHashSet( _tmp_fcontext, osrfStringArrayTokenize( prop_str, '.' ), "jump" );
385 // Tokenize field attribute into an osrfStringArray
386 char * field_list = (char*) xmlGetProp(_f, BAD_CAST "field");
388 osrfLogDebug(OSRF_LOG_MARK,
389 "Permacrud foreign context field list is %s", field_list );
390 map = osrfStringArrayTokenize( field_list, ' ' );
391 osrfHashSet( _tmp_fcontext, map, "context");
392 xmlFree( field_list );
394 // Insert the new hash into a hash attached to the parent node
395 osrfHashSet( foreign_context, _tmp_fcontext, osrfHashGet( _flink, "class" ) );
399 if( (prop_str = (char*)xmlGetNoNsProp(_f, BAD_CAST "field") )) {
400 char* map_list = prop_str;
401 osrfLogDebug(OSRF_LOG_MARK,
402 "Permacrud local context field list is %s", prop_str );
404 if (strlen( map_list ) > 0) {
406 char* _map_class = strtok_r(map_list, " ", &st_tmp);
408 osrfHashGet( action_def_hash, "local_context"), _map_class);
410 while ((_map_class = strtok_r(NULL, " ", &st_tmp))) {
412 osrfHashGet( action_def_hash, "local_context"), _map_class);
427 if (!strcmp( (char*)_cur->name, "source_definition" )) {
429 if( (content_str = (char*)xmlNodeGetContent(_cur)) ) {
430 osrfLogDebug(OSRF_LOG_MARK, "Using source definition '%s' for class %s",
431 content_str, current_class_name );
448 osrfLogInfo(OSRF_LOG_MARK, "...IDL XML parsed");
453 // Adds a standard virtual field to a fields hash
454 static void add_std_fld( osrfHash* fields_hash, const char* field_name, unsigned pos ) {
455 char array_pos_buf[ 7 ];
456 osrfHash* std_fld_hash = osrfNewHash();
458 snprintf( array_pos_buf, sizeof( array_pos_buf ), "%u", pos );
459 osrfHashSet( std_fld_hash, strdup( array_pos_buf ), "array_position" );
460 osrfHashSet( std_fld_hash, "true", "virtual" );
461 osrfHashSet( std_fld_hash, strdup( field_name ), "name" );
462 osrfHashSet( fields_hash, std_fld_hash, field_name );
466 osrfHash* oilsIDLFindPath( const char* path, ... ) {
467 if(!path || strlen(path) < 1) return NULL;
469 osrfHash* obj = idlHash;
471 VA_LIST_TO_STRING(path);
478 token = strtok_r(t, "/", &tt);
479 if(!token) return NULL;
482 obj = osrfHashGet(obj, token);
483 } while( (token = strtok_r(NULL, "/", &tt)) && obj);
488 static osrfHash* findClassDef( const char* classname ) {
489 if( !classname || !idlHash )
492 return osrfHashGet( idlHash, classname );
495 osrfHash* oilsIDL_links( const char* classname ) {
496 osrfHash* classdef = findClassDef( classname );
498 return osrfHashGet( classdef, "links" );
503 osrfHash* oilsIDL_fields( const char* classname ) {
504 osrfHash* classdef = findClassDef( classname );
506 return osrfHashGet( classdef, "fields" );
511 int oilsIDL_classIsFieldmapper ( const char* classname ) {
512 if( findClassDef( classname ) )
518 // For a given class: return the array_position associated with a
519 // specified field. (or -1 if it doesn't exist)
520 int oilsIDL_ntop (const char* classname, const char* fieldname) {
521 osrfHash* fields_hash = oilsIDL_fields( classname );
523 return -1; // No such class, or no fields for it
525 osrfHash* field_def_hash = osrfHashGet( fields_hash, fieldname );
526 if( !field_def_hash )
527 return -1; // No such field
529 const char* pos_attr = osrfHashGet( field_def_hash, "array_position" );
531 return -1; // No array_position attribute
533 return atoi( pos_attr ); // Return position as int
536 // For a given class: return a copy of the name of the field
537 // at a specified array_position (or NULL if there is none)
538 char * oilsIDL_pton (const char* classname, int pos) {
539 osrfHash* fields_hash = oilsIDL_fields( classname );
541 return NULL; // No such class, or no fields for it
544 osrfHash* field_def_hash = NULL;
545 osrfHashIterator* iter = osrfNewHashIterator( fields_hash );
547 while ( ( field_def_hash = osrfHashIteratorNext( iter ) ) ) {
548 if ( atoi( osrfHashGet( field_def_hash, "array_position" ) ) == pos ) {
549 ret = strdup( osrfHashIteratorKey( iter ) );
554 osrfHashIteratorFree( iter );