src/midgard_core_object.c

00001 /* 
00002  * Copyright (C) 2006 Piotr Pokora <piotr.pokora@infoglob.pl>
00003  *
00004  * This program is free software; you can redistribute it and/or modify it
00005  * under the terms of the GNU Lesser General Public License as published
00006  * by the Free Software Foundation; either version 2 of the License, or
00007  * (at your option) any later version.
00008  * 
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  * 
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software
00016  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017  */
00018 
00019 #include <midgard/midgard.h>
00020 #include <midgard/midgard_legacy.h>
00021 #include <libxml/parser.h> 
00022 #include <libxml/tree.h>
00023 #include <libxml/xmlreader.h>
00024 #include <libxml/parserInternals.h>
00025 
00026 static const gchar *MIDGARD_OBJECT_HREF = "http://www.midgard-project.org/midgard_object/1.8";
00027 
00028 static void _write_nodes(GObject *object, xmlNodePtr node)
00029 {
00030         g_assert(object);
00031         g_assert(node);
00032 
00033         guint prop_n;
00034         MgdObject *mgdobject = (MgdObject *) object;
00035         MidgardReflectionProperty *mrp = NULL;
00036         GParamSpec **pspec = g_object_class_list_properties(
00037                         G_OBJECT_GET_CLASS(G_OBJECT(object)), &prop_n);
00038         
00039         if(MIDGARD_IS_OBJECT(object)) {
00040                 MidgardObjectClass *klass = 
00041                         MIDGARD_OBJECT_GET_CLASS(object);
00042                 if(klass)
00043                         mrp = midgard_reflection_property_new(klass);
00044         }
00045 
00046         GValue pval = {0, }, *lval;
00047         GString *gstring;
00048         gchar *strprop = NULL;
00049         xmlChar *escaped;
00050         guint i;
00051         xmlNodePtr op_node = NULL;
00052         const gchar *linktype;
00053         MidgardCollector *mc;
00054         guint _uint;
00055         gint _int;
00056 
00057         for(i = 0; i < prop_n; i++) {
00058                 
00059                 g_value_init(&pval,pspec[i]->value_type);
00060                 g_object_get_property(G_OBJECT(object), 
00061                                 pspec[i]->name, &pval);
00062 
00063                 /* If property is a link we need to query guid 
00064                  * which identifies link object. Only if property 
00065                  * is not of guid or string type */
00066                 if(mrp){
00067                         if(midgard_reflection_property_is_link(mrp, 
00068                                                 pspec[i]->name)){
00069 
00070                                 lval = g_new0(GValue, 1);
00071                                 switch(pspec[i]->value_type) {
00072                                         
00073                                         case G_TYPE_UINT:
00074                                                 g_value_init(lval, G_TYPE_UINT);
00075                                                 _uint = g_value_get_uint(&pval);
00076                                                 if(!_uint){
00077                                                         g_value_unset(lval);
00078                                                         goto export_unchecked_property;
00079                                                 }
00080                                                 g_value_set_uint(lval, _uint);
00081                                                 break;
00082 
00083                                         case G_TYPE_INT:
00084                                                 g_value_init(lval, G_TYPE_INT);
00085                                                 _int = g_value_get_int(&pval);
00086                                                 if(!_int){
00087                                                         g_value_unset(lval);
00088                                                         goto export_unchecked_property;
00089                                                 }
00090                                                 g_value_set_int(lval, _int);
00091                                                 break;
00092 
00093                                         default:
00094                                                 goto export_unchecked_property;
00095                                 }
00096                                         
00097                                 linktype = 
00098                                         midgard_reflection_property_get_link_name(
00099                                                         mrp, pspec[i]->name);
00100                                 
00101                                 if(linktype){
00102                                         mc = midgard_collector_new(
00103                                                         mgdobject->mgd->_mgd,
00104                                                         linktype,
00105                                                         "id",
00106                                                         lval);
00107                                         
00108                                         midgard_collector_set_key_property(
00109                                                         mc,
00110                                                         "guid", NULL);
00111                                         if(!midgard_collector_execute(mc)){
00112                                                 g_object_unref(mc);
00113                                                 g_value_unset(&pval);
00114                                                 continue;
00115                                         }                               
00116                                         const gchar **linkguid = 
00117                                                 midgard_collector_list_keys(mc);
00118                                         if(linkguid[0])
00119                                                 strprop = g_strdup(linkguid[0]);
00120                                         if(!strprop)
00121                                                 strprop = g_strdup("");
00122                                         
00123                                         /* Create node */
00124                                         escaped = xmlEncodeEntitiesReentrant(
00125                                                         NULL, (const xmlChar*)strprop);
00126                                         xmlNewTextChild(node, NULL,
00127                                                         BAD_CAST pspec[i]->name,
00128                                                         BAD_CAST escaped);
00129                                         
00130                                         g_free(linkguid);
00131                                         g_free(strprop);
00132                                         g_free(escaped);
00133                                         g_object_unref(mc);     
00134                                 }
00135                                 g_value_unset(&pval);
00136                                 continue;
00137                         }
00138                 }       
00139                 
00140                 export_unchecked_property:
00141                 switch(pspec[i]->value_type) {
00142                         
00143                         case G_TYPE_STRING:                             
00144                                 strprop = g_value_dup_string(&pval);
00145                                 if(!strprop)
00146                                         strprop = g_strdup("");
00147                                 escaped = xmlEncodeEntitiesReentrant(
00148                                                 NULL, (const xmlChar*)strprop);
00149                                 xmlNewTextChild(node, NULL, 
00150                                                 BAD_CAST pspec[i]->name,
00151                                                 BAD_CAST escaped);
00152                                  
00153                                 /* This is commented because I have no idea how to use CDATA
00154                                  * block between property tags. And more, CDATA is not 
00155                                  * recomended for such usage ( at least by libxml developers )
00156                                   
00157                                 xmlNodePtr cdatanode = 
00158                                         xmlNewCDataBlock(NULL, 
00159                                                         (const xmlChar *) strprop,
00160                                                         xmlStrlen((const xmlChar *)strprop));
00161                                 xmlNodeSetName(cdatanode, (const xmlChar *)pspec[i]->name);
00162                                 xmlSetProp(cdatanode, "aprop", "apropval");
00163                                 xmlAddChild(node, cdatanode);
00164                                 */
00165                                 g_free(strprop);
00166                                 g_free(escaped);
00167                                 break;
00168 
00169                         case G_TYPE_INT:
00170                                 gstring = g_string_new("");
00171                                 g_string_append_printf(gstring,
00172                                                 "%d", g_value_get_int(&pval));
00173                                 strprop = g_string_free(gstring, FALSE);
00174                                 xmlNewChild(node, NULL, 
00175                                                 BAD_CAST pspec[i]->name,
00176                                                 BAD_CAST (xmlChar *)strprop);
00177                                 g_free(strprop);
00178                                 break;
00179 
00180                         case G_TYPE_UINT:
00181                                 if(!g_str_equal(pspec[i]->name, 
00182                                                         "sitegroup")) {
00183                                         gstring = g_string_new("");
00184                                         g_string_append_printf(gstring, 
00185                                                         "%i", g_value_get_uint(&pval));
00186                                         strprop = g_string_free(gstring, FALSE);
00187                                         xmlNewChild(node, NULL,
00188                                                         BAD_CAST pspec[i]->name,
00189                                                         BAD_CAST (xmlChar *)strprop);
00190                                         g_free(strprop);
00191                                 }
00192                                 break;
00193 
00194                         case G_TYPE_FLOAT:
00195                                 gstring = g_string_new("");
00196                                 g_string_append_printf(gstring,
00197                                                 "%g", g_value_get_float(&pval));
00198                                 strprop = g_string_free(gstring, FALSE);
00199                                 xmlNewChild(node, NULL,
00200                                                 BAD_CAST pspec[i]->name,
00201                                                 BAD_CAST (xmlChar *)strprop);
00202                                 g_free(strprop);
00203                                 break;
00204 
00205                         case G_TYPE_BOOLEAN:
00206                                 if(g_value_get_boolean(&pval))
00207                                         strprop = "1";
00208                                 else 
00209                                         strprop = "0";
00210 
00211                                 xmlNewChild(node, NULL,
00212                                                 BAD_CAST pspec[i]->name,
00213                                                 BAD_CAST (xmlChar *)strprop);
00214                                 break;
00215 
00216                         case G_TYPE_OBJECT:
00217                                 op_node = xmlNewNode(NULL, 
00218                                                 BAD_CAST pspec[i]->name);
00219                                 _write_nodes(G_OBJECT(g_value_get_object(&pval)), 
00220                                                 op_node);
00221                                 xmlAddChild(node, op_node);
00222                                 break;
00223                 }
00224                 g_value_unset(&pval);
00225         }
00226         g_free(pspec);
00227         if(mrp)
00228                 g_object_unref(mrp);
00229 }               
00230 
00231 gchar *_midgard_core_object_to_xml(MgdObject *object)
00232 {
00233         g_assert(object);
00234         
00235         xmlDocPtr doc = NULL; 
00236         xmlNodePtr root_node = NULL;
00237                 
00238         LIBXML_TEST_VERSION;
00239                 
00240         doc = xmlNewDoc(BAD_CAST "1.0");
00241         root_node = xmlNewNode(NULL, 
00242                         BAD_CAST "midgard_object");
00243         xmlNewNs(root_node,
00244                         BAD_CAST MIDGARD_OBJECT_HREF,
00245                         NULL);
00246         xmlNodePtr object_node = 
00247                 xmlNewNode(NULL, BAD_CAST G_OBJECT_TYPE_NAME(G_OBJECT(object)));
00248         xmlAddChild(root_node, object_node);
00249         xmlDocSetRootElement(doc, root_node);
00250 
00251         _write_nodes(G_OBJECT(object), object_node);
00252         
00253         xmlChar *buf;
00254         gint size;
00255         xmlDocDumpFormatMemoryEnc(doc, &buf, &size, "UTF-8", 1);
00256         
00257         xmlFreeDoc(doc);
00258         xmlCleanupParser();
00259 
00260         return (gchar*) buf;
00261 }
00262 
00263 
00264 static xmlNode *_get_type_node(xmlNode *node)
00265 {
00266         xmlNode *cur = NULL;    
00267         for (cur = node; cur; cur = cur->next) {
00268                 if (cur->type == XML_ELEMENT_NODE)
00269                         return cur;
00270         }
00271         return NULL;
00272 }
00273 
00274 static gboolean _nodes2object(GObject *object, xmlNode *node)
00275 {
00276         g_assert(object);
00277         g_assert(node);
00278 
00279         xmlNode *cur = NULL;
00280         GObject *prop_object;
00281         gchar *nodeprop;
00282         xmlChar *decoded;
00283         xmlParserCtxtPtr parser;
00284         MgdObject *mobject = MIDGARD_OBJECT(object);
00285         MgdObject *lobject = NULL;
00286         MidgardReflectionProperty *mrp = NULL;
00287         const gchar *linktype = NULL;
00288 
00289         if(MIDGARD_IS_OBJECT(object)) {
00290                 MidgardObjectClass *klass =
00291                         MIDGARD_OBJECT_GET_CLASS(mobject);
00292                 if(klass)
00293                         mrp = midgard_reflection_property_new(klass);
00294         }
00295 
00296 
00297         for (cur = node; cur; cur = cur->next) {
00298                 if (cur->type == XML_ELEMENT_NODE) {
00299                         
00300                         GParamSpec *pspec = g_object_class_find_property(
00301                                         G_OBJECT_GET_CLASS(G_OBJECT(object)), 
00302                                         (const gchar *)cur->name);
00303                         if(pspec) {
00304                                 GValue pval = {0, };
00305                                 g_value_init(&pval, pspec->value_type);
00306                                 nodeprop = (gchar *)xmlNodeGetContent(cur);
00307 
00308                                 if(mrp) {
00309                                         if(midgard_reflection_property_is_link(
00310                                                                 mrp, pspec->name)){
00311                                                 linktype =
00312                                                         midgard_reflection_property_get_link_name(
00313                                                                         mrp, pspec->name);
00314                                         }
00315                                 }
00316 
00317                                 /* moved out from mrp condition check to avoid nested indents */
00318                                 if(linktype && midgard_is_guid(
00319                                                         (const gchar *) nodeprop)){
00320 
00321                                         /* we can use nodeprop directly */
00322                                         lobject = midgard_get_object_by_guid(
00323                                                         mobject->mgd->_mgd,
00324                                                         (const gchar *) nodeprop);
00325 
00326                                         if(!lobject){
00327                                                 g_object_unref(mrp);
00328                                                 g_value_unset(&pval);
00329                                                 midgard_set_error(mobject->mgd->_mgd, 
00330                                                                 MGD_GENERIC_ERROR, 
00331                                                                 MGD_ERR_MISSED_DEPENDENCE, 
00332                                                                 " Can not import %s. "
00333                                                                 "No '%s' object identified by '%s'",
00334                                                                 G_OBJECT_TYPE_NAME(object),
00335                                                                 linktype, nodeprop); 
00336                                                 g_clear_error(&mobject->mgd->_mgd->err);
00337                                                 g_warning("%s", mobject->mgd->_mgd->errstr);
00338                                                 return FALSE;
00339                                         }
00340 
00341                                         GValue tval = {0, };
00342                                         g_value_init(&tval, G_TYPE_UINT);
00343 
00344                                         g_object_get_property(G_OBJECT(lobject),
00345                                                         "id", &tval);
00346 
00347                                         if(G_VALUE_TYPE(&pval) == G_TYPE_INT)
00348                                                 g_value_transform((const GValue *) &tval,
00349                                                                 &pval);
00350                                         else
00351                                                 g_value_copy((const GValue*) &tval,
00352                                                                 &pval);
00353         
00354                                         g_object_unref(lobject);
00355                                         g_value_unset(&tval);
00356                                         continue;                                       
00357                                 }
00358 
00359                                 switch (pspec->value_type) {
00360                                 
00361                                         case G_TYPE_STRING:
00362                                                 parser = xmlNewParserCtxt();
00363                                                 decoded = 
00364                                                         xmlStringDecodeEntities(parser,
00365                                                                         (const xmlChar *) nodeprop, 
00366                                                                         XML_SUBSTITUTE_PEREF |
00367                                                                         XML_SUBSTITUTE_REF, 
00368                                                                         0, 0, 0);
00369                                                 g_value_set_string(&pval, 
00370                                                                 (gchar *)decoded);
00371                                                 g_free(decoded);
00372                                                 xmlClearParserCtxt(parser);
00373                                                 g_free(parser);
00374                                                 break;
00375 
00376                                         case G_TYPE_INT:
00377                                                 if(nodeprop)
00378                                                         g_value_set_int(&pval, 
00379                                                                         (gint)atoi((gchar *)nodeprop));
00380                                                 break;
00381 
00382                                         case G_TYPE_UINT:
00383                                                 if(nodeprop)
00384                                                         g_value_set_uint(&pval,
00385                                                                         (guint)atoi((gchar *)nodeprop));
00386                                                 break;
00387 
00388                                         case G_TYPE_FLOAT:
00389                                                 g_value_set_float(&pval,
00390                                                                 (gfloat)atof((gchar *)nodeprop));
00391                                                 break;
00392 
00393                                         case G_TYPE_BOOLEAN:
00394                                                 g_value_set_boolean(&pval,
00395                                                                 (gboolean)atoi((gchar*)nodeprop));
00396                                                 break;
00397 
00398                                         case G_TYPE_OBJECT:
00399                                                 g_object_get(G_OBJECT(object),
00400                                                                 (const gchar *) cur->name,
00401                                                                 &prop_object, NULL);
00402                                                 _nodes2object(prop_object, cur->children);
00403                                                 g_value_set_object(&pval, prop_object);
00404                                                 break;
00405 
00406                                         default:
00407                                                 /* do nothing */
00408                                                 break;                                          
00409                                 }
00410                                 g_object_set_property(
00411                                                 G_OBJECT(object), 
00412                                                 (const gchar *) cur->name, 
00413                                                 &pval);
00414                                 g_value_unset(&pval);
00415                         } else {
00416                                 g_warning("Undefined property '%s' for '%s'",
00417                                                 cur->name, G_OBJECT_TYPE_NAME(object));
00418                         }
00419                 }               
00420         }
00421 
00422         if(mrp)
00423                 g_object_unref(mrp);
00424 
00425         return TRUE;
00426 }
00427 
00428 MgdObject *_midgard_core_object_from_xml(MidgardConnection *mgd, gchar *xml)
00429 {
00430         if(xml == NULL)
00431                 g_warning("Creating Midgard Object from xml requires xml."
00432                                 "NULL passed");
00433         
00434         LIBXML_TEST_VERSION
00435 
00436         /* midgard_object.xml is a dummy base URL in this case */
00437         xmlDocPtr doc = 
00438                 xmlReadMemory(xml, strlen(xml), "midgard_object.xml", NULL, 0);
00439         if(!doc) {
00440                 g_warning("Can not parse given xml");
00441                 return NULL;
00442         }
00443 
00444         xmlNodePtr root = xmlDocGetRootElement(doc);
00445         if(!root) {
00446                 g_warning("Can not find root element for given xml");
00447                 xmlFreeDoc(doc);
00448                 return NULL;
00449         }
00450 
00451         /* TODO : Get version from href and fail if invalid version exists */
00452         if(!g_str_equal(root->ns->href, MIDGARD_OBJECT_HREF)
00453                         || !g_str_equal(root->name, "midgard_object")) {
00454                 g_warning("Skipping invalid midgard_object xml");
00455                 xmlFreeDoc(doc);
00456                 return NULL;
00457         }
00458         /* we can try to get type name , so we can create schema object instance */
00459         /* Piotras: Either I am blind or there is no explicit libxml2 API call
00460          * to get child node explicitly. That's why _get_type_node is used */
00461         xmlNodePtr child = _get_type_node(root->children);
00462         if(!child) {
00463                 g_warning("Can not get midgard type name from the given xml");
00464                 xmlFreeDoc(doc);
00465                 return NULL;
00466         }
00467         
00468         if(!g_type_from_name((const gchar *)child->name)) {
00469                 g_warning("Type %s is not initialized in type system", 
00470                                 root->name);
00471                 xmlFreeDoc(doc);
00472                 return NULL;
00473         }
00474         MgdObject *object = midgard_object_new(mgd->mgd, (const gchar *)child->name, NULL);
00475         if(!object) {
00476                 g_warning("Can not create %s instance", child->name);
00477                 xmlFreeDoc(doc);
00478                 return NULL;
00479         }
00480 
00481         if(!_nodes2object(G_OBJECT(object), child->children)) {
00482                 g_object_unref(object);
00483                 object = NULL;
00484         }
00485         
00486         xmlFreeDoc(doc);
00487         xmlCleanupParser();
00488 
00489         return object;
00490 }

Generated on Thu Feb 22 06:15:17 2007 for midgard-core by  doxygen 1.4.6