]> git.evergreen-ils.org Git - Evergreen.git/blob - Open-ILS/src/c-apps/oils_idl-core.c
LP1642337: Reporter Boolean Filters
[Evergreen.git] / Open-ILS / src / c-apps / oils_idl-core.c
1 #include "openils/oils_idl.h"
2 /*
3  * vim:noet:ts=4:
4  */
5
6 #include <stdlib.h>
7 #include <string.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>
14
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"
20
21 static xmlDocPtr idlDoc = NULL; // parse and store the IDL here
22
23 /* parse and store the IDL here */
24 static osrfHash* idlHash;
25
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 ) {
29
30         if (idlHash) return idlHash;
31
32         char* prop_str = NULL;
33
34         idlHash = osrfNewHash();
35         osrfHash* class_def_hash = NULL;
36
37         osrfLogInfo(OSRF_LOG_MARK, "Parsing the IDL XML...");
38         idlDoc = xmlReadFile( idl_filename, NULL, XML_PARSE_XINCLUDE );
39         
40         if (!idlDoc) {
41                 osrfLogError(OSRF_LOG_MARK, "Could not load or parse the IDL XML file!");
42                 return NULL;
43         }
44
45         osrfLogDebug(OSRF_LOG_MARK, "Initializing the Fieldmapper IDL...");
46
47         xmlNodePtr docRoot = xmlDocGetRootElement(idlDoc);
48         xmlNodePtr kid = docRoot->children;
49         while (kid) {
50                 if (!strcmp( (char*)kid->name, "class" )) {
51
52                         class_def_hash = osrfNewHash();
53                         char* current_class_name = (char*) xmlGetProp(kid, BAD_CAST "id");
54                         
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" );
58
59                         osrfHashSet( idlHash, class_def_hash, current_class_name );
60
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 );
63                                 osrfHashSet(
64                                         class_def_hash,
65                                         prop_str,
66                                         "tablename"
67                                 );
68                         }
69
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 );
72                                 osrfHashSet(
73                                         class_def_hash,
74                                         prop_str,
75                                         "restrict_primary"
76                                 );
77                         }
78
79                         if ((prop_str = (char*)xmlGetNsProp(kid, BAD_CAST "virtual", BAD_CAST PERSIST_NS))) {
80                                 osrfHashSet(
81                                         class_def_hash,
82                                         prop_str,
83                                         "virtual"
84                                 );
85                         }
86
87                         // Tokenize controller attribute into an osrfStringArray
88                         prop_str = (char*) xmlGetProp(kid, BAD_CAST "controller");
89                         if( prop_str )
90                                 osrfLogDebug(OSRF_LOG_MARK, "Controller list is %s", prop_str );
91                         osrfStringArray* controller = osrfStringArrayTokenize( prop_str, ' ' );
92                         xmlFree( prop_str );
93                         osrfHashSet( class_def_hash, controller, "controller");
94
95                         osrfHash* current_links_hash = osrfNewHash();
96                         osrfHash* current_fields_hash = osrfNewHash();
97
98                         osrfHashSet( class_def_hash, current_fields_hash, "fields" );
99                         osrfHashSet( class_def_hash, current_links_hash, "links" );
100
101                         xmlNodePtr _cur = kid->children;
102
103                         while (_cur) {
104
105                                 if (!strcmp( (char*)_cur->name, "fields" )) {
106
107                                         if( (prop_str = (char*)xmlGetNsProp(_cur, BAD_CAST "primary", BAD_CAST PERSIST_NS)) ) {
108                                                 osrfHashSet(
109                                                         class_def_hash,
110                                                         prop_str,
111                                                         "primarykey"
112                                                 );
113                                         }
114
115                                         if( (prop_str = (char*)xmlGetNsProp(_cur, BAD_CAST "sequence", BAD_CAST PERSIST_NS)) ) {
116                                                 osrfHashSet(
117                                                         class_def_hash,
118                                                         prop_str,
119                                                         "sequence"
120                                                 );
121                                         }
122
123                                         unsigned int array_pos = 0;
124                                         char array_pos_buf[ 7 ];  // For up to 1,000,000 fields per class
125
126                                         xmlNodePtr _f = _cur->children;
127                                         while(_f) {
128                                                 if (strcmp( (char*)_f->name, "field" )) {
129                                                         _f = _f->next;
130                                                         continue;
131                                                 }
132
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");
136                                                 if( field_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" ) ) {
142                                                                 free( field_name );
143                                                                 _f = _f->next;
144                                                                 continue;
145                                                         }
146                                                 } else {
147                                                         osrfLogDebug(OSRF_LOG_MARK,
148                                                                         "Found field with no name for class %s", current_class_name );
149                                                         _f = _f->next;
150                                                         continue;
151                                                 }
152  
153                                                 osrfHash* field_def_hash = osrfNewHash();
154
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" );
158
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");
164                                                 }
165
166                                                 if( (prop_str = (char*)xmlGetNsProp(_f, BAD_CAST "i18n", BAD_CAST PERSIST_NS)) ) {
167                                                         osrfHashSet(
168                                                                 field_def_hash,
169                                                                 prop_str,
170                                                                 "i18n"
171                                                         );
172                                                 }
173
174                                                 if( (prop_str = (char*)xmlGetNsProp(_f, BAD_CAST "virtual", BAD_CAST PERSIST_NS)) ) {
175                                                         osrfHashSet(
176                                                                 field_def_hash,
177                                                                 prop_str,
178                                                                 "virtual"
179                                                         );
180                                                 } else {   // default to virtual
181                                                         osrfHashSet(
182                                                                 field_def_hash,
183                                                                 "false",
184                                                                 "virtual"
185                                                         );
186                                                 }
187
188                                                 if( (prop_str = (char*)xmlGetNsProp(_f, BAD_CAST "primitive", BAD_CAST PERSIST_NS)) ) {
189                                                         osrfHashSet(
190                                                                 field_def_hash,
191                                                                 prop_str,
192                                                                 "primitive"
193                                                         );
194                                                 }
195
196                                                 osrfHashSet( field_def_hash, field_name, "name" );
197                                                 osrfHashSet(
198                                                         current_fields_hash,
199                                                         field_def_hash,
200                                                         field_name
201                                                 );
202                                                 _f = _f->next;
203                                         }
204
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   );
209
210                                 }
211
212                                 if (!strcmp( (char*)_cur->name, "links" )) {
213                                         xmlNodePtr _l = _cur->children;
214
215                                         while(_l) {
216                                                 if (strcmp( (char*)_l->name, "link" )) {
217                                                         _l = _l->next;
218                                                         continue;
219                                                 }
220
221                                                 osrfHash* link_def_hash = osrfNewHash();
222
223                                                 if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "reltype")) ) {
224                                                         osrfHashSet(
225                                                                 link_def_hash,
226                                                                 prop_str,
227                                                                 "reltype"
228                                                         );
229                                                         osrfLogDebug(OSRF_LOG_MARK, "Adding link with reltype %s", prop_str );
230                                                 } else
231                                                         osrfLogDebug(OSRF_LOG_MARK, "Adding link with no reltype" );
232
233                                                 if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "key")) ) {
234                                                         osrfHashSet(
235                                                                 link_def_hash,
236                                                                 prop_str,
237                                                                 "key"
238                                                         );
239                                                         osrfLogDebug(OSRF_LOG_MARK, "Link fkey is %s", prop_str );
240                                                 } else
241                                                         osrfLogDebug(OSRF_LOG_MARK, "Link with no fkey" );
242
243                                                 if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "class")) ) {
244                                                         osrfHashSet(
245                                                                 link_def_hash,
246                                                                 prop_str,
247                                                                 "class"
248                                                         );
249                                                         osrfLogDebug(OSRF_LOG_MARK, "Link fclass is %s", prop_str );
250                                                 } else
251                                                         osrfLogDebug(OSRF_LOG_MARK, "Link with no fclass" );
252
253                                                 // Tokenize map attribute into an osrfStringArray
254                                                 prop_str = (char*) xmlGetProp(_l, BAD_CAST "map");
255                                                 if( prop_str )
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");
259                                                 xmlFree( prop_str );
260
261                                                 if( (prop_str = (char*)xmlGetProp(_l, BAD_CAST "field")) ) {
262                                                         osrfHashSet(
263                                                                 link_def_hash,
264                                                                 prop_str,
265                                                                 "field"
266                                                         );
267                                                         osrfLogDebug(OSRF_LOG_MARK, "Link fclass is %s", prop_str );
268                                                 } else
269                                                         osrfLogDebug(OSRF_LOG_MARK, "Link with no fclass" );
270
271                                                 osrfHashSet(
272                                                         current_links_hash,
273                                                         link_def_hash,
274                                                         prop_str
275                                                 );
276
277                                                 _l = _l->next;
278                                         }
279                                 }
280 /**** Structure of permacrud in memory ****
281
282 { create :
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 ] }, ...}
288     },
289   retrieve : null, -- no perm check, or structure similar to the others
290   update : -- like create
291     ...
292   delete : -- like create
293     ...
294 }   
295
296 **** Structure of permacrud in memory ****/
297
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");
303
304                                         while(_l) {
305                                                 if (strcmp( (char*)_l->name, "actions" )) {
306                                                         _l = _l->next;
307                                                         continue;
308                                                 }
309
310                                                 xmlNodePtr _a = _l->children;
311
312                                                 while(_a) {
313                                                         const char* action_name = (const char*) _a->name;
314                                                         if (
315                                                                 strcmp( action_name, "create" ) &&
316                                                                 strcmp( action_name, "retrieve" ) &&
317                                                                 strcmp( action_name, "update" ) &&
318                                                                 strcmp( action_name, "delete" )
319                                                         ) {
320                                                                 _a = _a->next;
321                                                                 continue;
322                                                         }
323
324                                                         osrfLogDebug(OSRF_LOG_MARK, "Found Permacrud action %s for class %s",
325                                                                 action_name, current_class_name );
326
327                                                         osrfHash* action_def_hash = osrfNewHash();
328                                                         osrfHashSet( pcrud, action_def_hash, action_name );
329
330                                                         // Set the class-wide ignore_object_perms flag
331                                                 osrfHashSet( action_def_hash, ignore_object_perms, "ignore_object_perms");
332
333                                                         // Tokenize permission attribute into an osrfStringArray
334                                                         prop_str = (char*) xmlGetProp(_a, BAD_CAST "permission");
335                                                         if( prop_str )
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");
340                                                         xmlFree( prop_str );
341
342                                                 osrfHashSet( action_def_hash,
343                                                                 (char*)xmlGetNoNsProp(_a, BAD_CAST "owning_user"), "owning_user");
344
345                                                 osrfHashSet( action_def_hash,
346                                                                 (char*)xmlGetNoNsProp(_a, BAD_CAST "global_required"), "global_required");
347
348                                                         // Tokenize context_field attribute into an osrfStringArray
349                                                         prop_str = (char*) xmlGetProp(_a, BAD_CAST "context_field");
350                                                         if( prop_str )
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");
355                                                         xmlFree( prop_str );
356
357                                                         osrfHash* foreign_context = osrfNewHash();
358                                                         osrfHashSet( action_def_hash, foreign_context, "foreign_context");
359
360                                                         xmlNodePtr _f = _a->children;
361
362                                                         while(_f) {
363                                                                 if ( strcmp( (char*)_f->name, "context" ) ) {
364                                                                         _f = _f->next;
365                                                                         continue;
366                                                                 }
367
368                                                                 if( (prop_str = (char*)xmlGetNoNsProp(_f, BAD_CAST "link")) ) {
369                                                                         osrfLogDebug(OSRF_LOG_MARK,
370                                                                                 "Permacrud context link definition is %s", prop_str );
371
372                                                                         osrfHash* _tmp_fcontext = osrfNewHash();
373
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" );
379                                                                         xmlFree( prop_str );
380
381                                                                     if( (prop_str = (char*)xmlGetNoNsProp(_f, BAD_CAST "jump")) )
382                                                                             osrfHashSet( _tmp_fcontext, osrfStringArrayTokenize( prop_str, '.' ), "jump" );
383                                                                         xmlFree( prop_str );
384
385                                                                         // Tokenize field attribute into an osrfStringArray
386                                                                         char * field_list = (char*) xmlGetProp(_f, BAD_CAST "field");
387                                                                         if( field_list )
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 );
393
394                                                                         // Insert the new hash into a hash attached to the parent node
395                                                                         osrfHashSet( foreign_context, _tmp_fcontext, osrfHashGet( _flink, "class" ) );
396
397                                                                 } else {
398
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 );
403                         
404                                                                                 if (strlen( map_list ) > 0) {
405                                                                                         char* st_tmp = NULL;
406                                                                                         char* _map_class = strtok_r(map_list, " ", &st_tmp);
407                                                                                         osrfStringArrayAdd(
408                                                                                                 osrfHashGet( action_def_hash, "local_context"), _map_class);
409                                                                         
410                                                                                         while ((_map_class = strtok_r(NULL, " ", &st_tmp))) {
411                                                                                                 osrfStringArrayAdd(
412                                                                                                         osrfHashGet( action_def_hash, "local_context"), _map_class);
413                                                                                         }
414                                                                                 }
415                                                                                 xmlFree(map_list);
416                                                                         }
417
418                                                                 }
419                                                                 _f = _f->next;
420                                                         }
421                                                         _a = _a->next;
422                                                 }
423                                                 _l = _l->next;
424                                         }
425                                 }
426
427                                 if (!strcmp( (char*)_cur->name, "source_definition" )) {
428                                         char* content_str;
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 );
432                                                 osrfHashSet(
433                                                         class_def_hash,
434                                                         content_str,
435                                                         "source_definition"
436                                                 );
437                                         }
438
439                                 }
440
441                                 _cur = _cur->next;
442                         } // end while
443                 }
444
445                 kid = kid->next;
446         } // end while
447
448         osrfLogInfo(OSRF_LOG_MARK, "...IDL XML parsed");
449
450         return idlHash;
451 }
452
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();
457
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 );
463 }
464
465
466 osrfHash* oilsIDLFindPath( const char* path, ... ) {
467         if(!path || strlen(path) < 1) return NULL;
468
469         osrfHash* obj = idlHash;
470
471         VA_LIST_TO_STRING(path);
472         char* buf = VA_BUF;
473
474         char* token = NULL;
475         char* t = buf;
476         char* tt;
477
478         token = strtok_r(t, "/", &tt);
479         if(!token) return NULL;
480
481         do {
482                 obj = osrfHashGet(obj, token);
483         } while( (token = strtok_r(NULL, "/", &tt)) && obj);
484
485         return obj;
486 }
487
488 static osrfHash* findClassDef( const char* classname ) {
489         if( !classname || !idlHash )
490                 return NULL;
491         else
492                 return osrfHashGet( idlHash, classname );
493 }
494
495 osrfHash* oilsIDL_links( const char* classname ) {
496         osrfHash* classdef = findClassDef( classname );
497         if( classdef )
498                 return osrfHashGet( classdef, "links" );
499         else
500                 return NULL;
501 }
502
503 osrfHash* oilsIDL_fields( const char* classname ) {
504         osrfHash* classdef = findClassDef( classname );
505         if( classdef )
506                 return osrfHashGet( classdef, "fields" );
507         else
508                 return NULL;
509 }
510
511 int oilsIDL_classIsFieldmapper ( const char* classname ) {
512         if( findClassDef( classname ) )
513                 return 1;
514         else
515                 return 0;
516 }
517
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 );
522         if( !fields_hash )
523                 return -1;     // No such class, or no fields for it
524
525         osrfHash* field_def_hash = osrfHashGet( fields_hash, fieldname );
526         if( !field_def_hash )
527                 return -1;                      // No such field
528
529         const char* pos_attr = osrfHashGet( field_def_hash, "array_position" );
530         if( !pos_attr )
531                 return -1;                      // No array_position attribute
532
533         return atoi( pos_attr );        // Return position as int
534 }
535
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 );
540         if( !fields_hash )
541                 return NULL;     // No such class, or no fields for it
542
543         char* ret = NULL;
544         osrfHash* field_def_hash = NULL;
545         osrfHashIterator* iter = osrfNewHashIterator( fields_hash );
546
547         while ( ( field_def_hash = osrfHashIteratorNext( iter ) ) ) {
548                 if ( atoi( osrfHashGet( field_def_hash, "array_position" ) ) == pos ) {
549                         ret = strdup( osrfHashIteratorKey( iter ) );
550                         break;
551                 }
552         }
553
554         osrfHashIteratorFree( iter );
555
556         return ret;
557 }
558