Main Page | Namespace List | Class Hierarchy | Alphabetical List | Data Structures | Directories | File List | Namespace Members | Data Fields | Globals

parseobj.cpp

Go to the documentation of this file.
00001 // See ../../license.txt for license information.
00002 //
00003 // parse.cpp
00004 //
00005 // NOTES
00006 //              XML Parser for the persistence framework.
00007 //
00008 // 30-Jun-2003  phamilton  Created
00009 //
00010 
00011 #define PERSIST_IN_LIBRARY_SOURCE
00012 
00013 #include <iostream>
00014 #include <fstream>
00015 #include <unistd.h> 
00016 #include <boost/lexical_cast.hpp>
00017 #include <boost/filesystem/path.hpp>
00018 #include <boost/filesystem/operations.hpp>
00019 #include <boost/bind.hpp>
00020 #include "parseobj.hpp"
00021 #include "../../common/object.hpp"
00022 #include "../../common/composition_object.hpp"
00023 #include "../../common/persistable_object.hpp"
00024 #include "../../common/outerable_object.hpp"
00025 #include "../../common/importable_object.hpp"
00026 
00027 using namespace ph::persist;
00028 
00029 // important strings
00030 const xmlstring s_objtype      = S("type");
00031 const xmlstring s_objname      = S("name");
00032 
00033 // possible errors.
00034 const std::string efmt_bad_stack       = S("Expected an entry on the object stack.");
00035 const std::string efmt_bad_tag         = S("Unknown tag [%s].");
00036 const std::string efmt_fail_create     = S("Couldn't create object [%s (%s)].");
00037 const std::string efmt_fail_create_root = S("Couldn't create root object [%s (%s)].");
00038 const std::string efmt_fail_create_delayed = S("Couldn't create delayed object [%s (%s)].");
00039 const std::string efmt_fail_not_importable = S("Object created was not importable.");
00040 const std::string efmt_prop_not_comp   = S("Member is not composite object [%s(%s)].");
00041 const std::string efmt_bad_aggr_obj    = S("Composite did not accept object (wrong type?) [%s(%s)].");
00042 const std::string efmt_bad_import      = S("Could not import [%s].");
00043 const std::string efmt_bad_stream      = S("Could not create URL stream for [%s].");
00044 const std::string efmt_bad_urlstream   = S("Could not open URL stream for [%s].");
00045 const std::string efmt_bad_endimport   = S("Bad place for an end import tag [%s].");
00046 const std::string efmt_bad_fragment    = S("Bad place for a fragment tag [%s].");
00047 const std::string efmt_bad_endfragment = S("Bad place for an end fragment tag [%s].");
00048 const std::string efmt_bad_location    = S("Location not found for a fragment tag [%s].");
00049 const std::string efmt_no_interface    = S("Object has no %s interface [%s].");
00050 const std::string efmt_not_composite    = S("Object is not composite [%s].");
00051 const std::string efmt_bad_data        = S("Tried to set a member [%s] with bad data [%s].");
00052 const std::string efmt_bad_path        = S("Bad pathvar encountered [%s] with [%s].");
00053 const std::string efmt_bad_ref_location    = S("Location not found for a reference [%s].");
00054 const std::string efmt_end_stack       = S("At the end, the stack still had elements. top: [%s]. "
00055                                                                                         "Check for unbalanced tags.");
00056 const std::string efmt_import_is_root  = S("An import is at the root of the file.");
00057 
00058 xml::parseobj::parseobj(parseobj *outer, std::istream *stream, const boost::filesystem::path &streampath, 
00059                 ph::common::persistable_object_context *context,
00060                 std::ostream *console, const boost::filesystem::path &rootpath, bool silent, unsigned int debug) :
00061         parse(console, silent)
00062 {
00063         _outer = outer;
00064         _stream = stream;
00065         
00066         // make sure that the streampath passed in is complete.
00067         _streampath = boost::filesystem::system_complete(streampath);
00068         _context = context;
00069         _rootpath = rootpath;
00070         _debug = debug;
00071         
00072         _obj = 0;
00073         _root = 0;
00074         _rootouter = 0;
00075         _cdata = "";
00076         _comment = "";
00077         _abort = false;
00078         _inmember = false;
00079         _anonymousobjnum = 0;
00080         _import = 0;
00081         _rootname = "";
00082         _fragment = false;
00083         _delayed_obj = 0;
00084 }
00085 
00086 static std::string obj_name(ph::common::object_base *obj)
00087 {
00088         std::string name;
00089         if (get_persistable_obj_name(obj, &name))
00090                 return name;
00091         else
00092                 return "No name for object";
00093 }
00094 
00095 static std::string obj_type(ph::common::object_base *obj)
00096 {
00097         std::string type;
00098         if (get_persistable_obj_type(obj, &type))
00099                 return type;
00100         else
00101                 return "No type for object";
00102 }
00103 
00104 void xml::parseobj::finish_handler()
00105 {
00106         // we have finished. Make sure that the stack is empty and that we have successfully
00107         // stashed an object.
00108         if (!_abort && !_stack.empty())
00109                 error(efmt_end_stack, obj_name(_stack.top()), true);
00110 }
00111 
00112 void xml::parseobj::startelement_handler(const xmlstring &element, const std::vector<xmlstring> &atts)
00113 {
00114         if (_abort)
00115                 return;
00116                 
00117         if (_debug & (PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00118                 *_errorhandler << ">> startelement : [" << element << "]" << std::endl;
00119 
00120         xmlstring arg = attr(atts, 0);
00121         
00122         if (arg.empty())
00123         {
00124                 if (_stack.empty())
00125                 {
00126                         error(efmt_bad_stack, true);
00127                         _abort = true;
00128                         return;
00129                 }
00130 
00131                 ph::common::object_base *top = _stack.top();
00132                 if (!test_persistable(top))
00133                         return;
00134                 
00135                 if (top->persistable()->has(element))
00136                 {
00137                         // the top has this as an element, so see if it's a composite element,
00138                         // then push that, or we are starting a member,
00139                         ph::common::object_base *c = top->persistable()->get_composite_object(element);
00140                         if (c)
00141                         {
00142                                 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00143                                         obj_out("pushing", c);
00144                                 _stack.push(c);
00145                         }
00146                         else
00147                         {
00148                                 // a normal member
00149                                 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00150                                         element_out("in member", element);
00151                                 _inmember = true;
00152                         }
00153                         if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00154                                 *_errorhandler << ">> emptying cdata." << std::endl;
00155                         _cdata = "";
00156                 }
00157                 else
00158                 {
00159                         // it's possible that we are trying to build an unnamed ph::common::object_base.
00160                         ph::common::object_base *obj = create_object(_stack.top(), element, 
00161                                 S("_") + boost::lexical_cast<std::string>(_anonymousobjnum++));
00162                         if (!obj)
00163                         {
00164                                 error(efmt_bad_tag, element, true);
00165                                 _abort = true;
00166                                 return;
00167                         }
00168 
00169                         if (_debug & PARSEOBJ_OBJECT_DEBUG)
00170                         {
00171                                 obj_out("composite (1)", _stack.top());
00172                                 obj_out("adding to composite (1)", obj);
00173                         }
00174                         if (!test_composition(_stack.top()))
00175                                 return;
00176                         if (!_stack.top()->composition()->composite()->add(obj, true))
00177                         {
00178                                 error(efmt_bad_aggr_obj, obj_name(_stack.top()), obj_type(_stack.top()), true);
00179                                 _abort = true;
00180                                 return;
00181                         }
00182                         
00183                         if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00184                                 obj_out("pushing anonymous", obj);
00185                         _stack.push(obj);
00186                 }
00187         }
00188         else if (_import)
00189         {
00190                 // in the middle of an import.
00191                 // we can expect...
00192                 // name value=
00193                 // param name= value
00194 
00195                 if (element == "name")
00196                         _import->_rootname = expectedattr(atts, "value");
00197                 else if (element == "param")
00198                 {
00199                         // add parameter, do subs on the name and the value.
00200                         _import->add_param(
00201                                 dosubs(expectedattr(atts, "name")), 
00202                                 dosubs(expectedattr(atts, "value")));
00203                 }
00204                 else if (element == "pathparam")
00205                 {
00206                         // add parameter, do subs on the name and the value
00207                         // and push them back. We convert relative paths
00208                         // to absolute.
00209                         std::string pathstr = dosubs(expectedattr(atts, "value"));
00210                         
00211                         try
00212                         {
00213                                 boost::filesystem::path p = pathstr;
00214                                 if (!p.is_complete())
00215                                         p = _streampath.branch_path() / p;
00216                                 _import->add_param(dosubs(expectedattr(atts, "name")), p.string());
00217                         }
00218                         catch (...)
00219                         {
00220                                 error(efmt_bad_path, element, pathstr, true);
00221                                 _abort = true;
00222                                 return;
00223                         }       
00224                 }
00225         }
00226         else if (_delayed_obj)
00227         {
00228                 // in the middle of an delayed import.
00229                 // we can expect...
00230                 // name value=
00231                 // param name= value
00232 
00233                 if (element == "name")
00234                 {
00235                         // set the root name for the import object.
00236                         _delayed_obj->setname(expectedattr(atts, "value"));
00237                 }
00238                 else if (element == "param")
00239                 {
00240                         // add parameter, do subs on the name and the value.
00241                         _delayed_obj->addparam(
00242                                 dosubs(expectedattr(atts, "name")), 
00243                                 dosubs(expectedattr(atts, "value")));
00244                 }
00245                 else if (element == "pathparam")
00246                 {
00247                         // add parameter, do subs on the name and the value
00248                         // and push them back. We convert relative paths
00249                         // to absolute.
00250                         std::string pathstr = dosubs(expectedattr(atts, "value"));
00251                         
00252                         try
00253                         {
00254                                 boost::filesystem::path p = pathstr;
00255                                 if (!p.is_complete())
00256                                         p = _streampath.branch_path() / p;
00257                                 _delayed_obj->addparam(dosubs(expectedattr(atts, "name")), p.string());
00258                         }
00259                         catch (...)
00260                         {
00261                                 error(efmt_bad_path, element, pathstr, true);
00262                                 _abort = true;
00263                                 return;
00264                         }
00265                 }
00266         }
00267         else
00268         {
00269                 // an element with attributes
00270                 // now check the tag.
00271                 if (element == S("import") || element == S("xi:include"))
00272                 {
00273                         xmlstring url = attr(atts, "href");
00274                         // allow for href or url.
00275                         if (url.empty())
00276                                 url = expectedattr(atts, "url");
00277                         xmlstring name = attr(atts, "name");
00278                         
00279                         // try any string substitutions on the value (for $() stuff).
00280                         url = dosubs(url);
00281                         name = dosubs(name);
00282 
00283                         if (_context->delayed_import())
00284                         {
00285                                 // we want to import the XML passed.
00286                                 // create a substream of the current stream for the import.
00287                                 boost::filesystem::path p(url, boost::filesystem::no_check);
00288                                 if (!p.is_complete())
00289                                         p = _streampath.branch_path() / p;
00290 
00291                                 // create a proxy object to handle parseing during import.
00292                                 ph::common::object_base *obj = _context->create_delayed(_errorhandler,
00293                                         p, name, _debug);
00294                                 if (obj)
00295                                 {
00296                                         // delayed objects need an importable interface, since 
00297                                         // our XML files can have names and parameters.
00298                                         _delayed_obj = obj->importable();
00299                                         if (!_delayed_obj)
00300                                         {
00301                                                 error(efmt_fail_not_importable, true);
00302                                                 _abort = true;
00303                                                 return;
00304                                         }
00305                                 }
00306                                 else
00307                                 {
00308                                         error(efmt_fail_create_delayed, url, name, true);
00309                                         _abort = true;
00310                                         return;
00311                                 }
00312 
00313                                 if (_stack.empty())
00314                                 {
00315                                         error(efmt_import_is_root, true);
00316                                         _abort = true;
00317                                         return;
00318                                 }
00319                                 else
00320                                 {
00321                                         // if the object on the top of the stack is a composite, then add this object to that.
00322                                         if (!test_composition(_stack.top()))
00323                                                 return;
00324                                         if (_debug & PARSEOBJ_OBJECT_DEBUG)
00325                                         {
00326                                                 obj_out("composite (2)", _stack.top());
00327                                                 obj_out("adding to composite (2)", obj);
00328                                         }
00329                                         if (!_stack.top()->composition()->composite()->add(obj, true))
00330                                         {
00331                                                 error(efmt_bad_aggr_obj, obj_name(_stack.top()), obj_type(_stack.top()), true);
00332                                                 _abort = true;
00333                                                 return;
00334                                         }
00335                                 }
00336                                 if (_debug & PARSEOBJ_DELAYED_DEBUG)
00337                                         *_errorhandler << ">> [" << p.string() << "] delaying." << std::endl;
00338                                 _stack.push(obj);
00339                         }
00340                         else
00341                         {
00342                                 if (_import)
00343                                 {
00344                                         error(efmt_bad_import, url, true);
00345                                         _abort = true;
00346                                         return;
00347                                 }
00348         
00349                                 // we want to import the XML passed.
00350                                 // create a substream of the current stream for the import.
00351                                 boost::filesystem::path p(url, boost::filesystem::no_check);
00352                                 _importstreampath = p;
00353                                 if (!_importstreampath.is_complete())
00354                                         _importstreampath = _streampath.branch_path() / _importstreampath;
00355                                 _importstream = new std::ifstream(_importstreampath.native_file_string().c_str());
00356                                 
00357                                 // create a new importer.
00358                                 _import = new parseobj(this, _importstream, _importstreampath, _context, 
00359                                         _errorhandler, _rootpath, _silent, _debug);
00360         
00361                                 if (name != "")
00362                                         _import->_rootname = name;
00363                         }
00364                 }
00365                 else if (element == S("fragment"))
00366                 {
00367                         xmlstring location = expectedattr(atts, "location");
00368                         if (_fragment)
00369                         {
00370                                 error(efmt_bad_fragment, location, true);
00371                                 _abort = true;
00372                                 return;
00373                         }
00374 
00375                         ph::common::object_base *c = _context->find_composite_object(_root, _root, location);
00376                         if (!c)
00377                         {
00378                                 error(efmt_bad_location, location, true);
00379                                 _abort = true;
00380                                 return;
00381                         }
00382                         
00383                         if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00384                                 obj_out("pushing composite", c);
00385                         _stack.push(c);
00386                         _fragment = true;
00387                 }
00388                 else if (element == S("var"))
00389                 {
00390                         // do subs on the name and the value.
00391                         // and push them back.
00392                         add_param(
00393                                 dosubs(expectedattr(atts, "name")), 
00394                                 dosubs(expectedattr(atts, "value")));
00395                 }
00396                 else if (element == S("pathvar"))
00397                 {
00398                         // do subs on the name and the value.
00399                         // and push them back. We convert relative paths
00400                         // to absolute.
00401                         std::string pathstr = dosubs(expectedattr(atts, "value"));
00402                         
00403                         try
00404                         {
00405                                 boost::filesystem::path p = pathstr;
00406                                 if (!p.is_complete())
00407                                         p = _streampath.branch_path() / p;
00408                                 add_param(dosubs(expectedattr(atts, "name")), p.string());
00409                         }
00410                         catch (...)
00411                         {
00412                                 error(efmt_bad_path, element, pathstr, true);
00413                                 _abort = true;
00414                                 return;
00415                         }       
00416                 }
00417                 else
00418                 {
00419                         if (arg == s_objname)
00420                         {
00421                                 xmlstring val = attrval(atts, 0);
00422                                 
00423                                 // starting an object. name is given.
00424                                 ph::common::object_base *obj = 0;
00425                                 
00426                                 if (_stack.empty())
00427                                 {
00428                                         // create the new object.
00429                                         // it's possible that we arer in the middle of parseing
00430                                         // some NEW objects into the middle of another file.
00431                                         
00432                                         // If there is an outer stack, then ask THAT object to create a new one,
00433                                         // otherwise just use the context.
00434                                         if (_outer && !_outer->_stack.empty())
00435                                                 obj = create_object(_outer->_stack.top(), element, val);
00436                                         
00437                                         if (!obj)                                               
00438                                                 obj = _context->create(element, val);
00439                                                 
00440                                         if (!obj)
00441                                         {
00442                                                 error(efmt_fail_create_root, element, val, true);
00443                                                 _abort = true;
00444                                                 return;
00445                                         }
00446         
00447                                         // save away this object as the root object.
00448                                         if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00449                                                 obj_out("stashing as root", obj);
00450                                                 
00451                                         _root = obj;
00452                                         
00453                                         // root objects can have an outer as well. This is especially
00454                                         // true in the case of delayed importing.
00455                                         if (_rootouter)
00456                                                 if (_root->outerable())
00457                                                         _root->outerable()->outer(_rootouter);
00458                                         
00459                                         // set the filepath for the root object.
00460                                         if (!test_persistable(_root))
00461                                                 return;
00462                                         _root->persistable()->set_file_path(_streampath.string());                      
00463                                         if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00464                                                 obj_out("setting the path to [" + _streampath.string() + "]", obj);
00465                                 }
00466                                 else
00467                                 {
00468                                         // if the object on the top of the stack is a composite, then add this object to that.
00469                                         if (!test_composition(_stack.top()))
00470                                                 return;
00471         
00472                                         // create the object.
00473                                         obj = create_object(_stack.top(), element, val);
00474                                         
00475                                         // object could not be created for some reason.
00476                                         if (!obj)
00477                                         {
00478                                                 error(efmt_fail_create, element, val, true);
00479                                                 _abort = true;
00480                                                 return;
00481                                         }
00482                                         
00483                                         if (_debug & PARSEOBJ_OBJECT_DEBUG)
00484                                         {
00485                                                 obj_out("composite (3)", _stack.top());
00486                                                 obj_out("adding to composite (3)", obj);
00487                                         }
00488 
00489                                         if (!_stack.top()->composition()->composite()->add(obj, true))
00490                                         {
00491                                                 error(efmt_bad_aggr_obj, obj_name(_stack.top()), obj_type(_stack.top()), true);
00492                                                 _abort = true;
00493                                                 return;
00494                                         }
00495                                 }
00496                                 
00497                                 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00498                                         obj_out("pushing obj with name", obj);
00499                                 _stack.push(obj);
00500                         }
00501                         else if (_stack.empty())
00502                         {
00503                                 error(efmt_bad_stack, true);
00504                                 _abort = true;
00505                         }
00506                         else
00507                         {
00508                                 error(efmt_bad_tag, element, true);
00509                                 _abort = true;
00510                         }
00511                 }
00512         }
00513 }
00514 
00515 void xml::parseobj::cdata_handler(const xmlstring &s, int len)
00516 {
00517         if (_abort)
00518                 return;
00519                 
00520         if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00521                 *_errorhandler << ">> cdata : [" << s << "]" << std::endl;
00522 
00523         _cdata += s;
00524 }
00525 
00526 void xml::parseobj::comment_handler(const xmlstring &s)
00527 {
00528         if (_abort)
00529                 return;
00530                 
00531         if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00532                 *_errorhandler << ">> comment : [" << s << "]" << std::endl;
00533 
00534         _comment = s;
00535 }
00536 
00537 void xml::parseobj::endelement_handler(const xmlstring &element)
00538 {
00539         if (_abort)
00540                 return;
00541                 
00542         if (_debug & (PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00543                 *_errorhandler << ">> endelement : [" << element << "]" << std::endl;
00544 
00545         if (_stack.empty())
00546         {
00547                 error(efmt_bad_stack, true);
00548                 _abort = true;
00549                 return;
00550         }
00551         
00552         if (element == S("import") || element == S("xi:include"))
00553         {
00554                 if (_context->delayed_import())
00555                 {
00556                         // add proxy object as the object.
00557                         if (!_delayed_obj)
00558                         {
00559                                 error(efmt_bad_endimport, element, true);
00560                                 _abort = true;
00561                                 return;
00562                         }
00563                         ph::common::object_base *obj = _stack.top();
00564                         if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00565                                 obj_out("popping because delayed", obj);
00566                         // if we are closing off a delayed, just pop it off.
00567                         _stack.pop();
00568                         _delayed_obj = 0;
00569                 }
00570                 else
00571                 {
00572                         if (!_import)
00573                         {
00574                                 error(efmt_bad_endimport, element, true);
00575                                 _abort = true;
00576                                 return;
00577                         }
00578         
00579                         if (!parse::parse_xml(_importstream, _importstreampath.string(), _import, 0))
00580                         {
00581                                 error(efmt_bad_urlstream, element, true);
00582                                 _abort = true;
00583                                 return;
00584                         }
00585         
00586                         ph::common::object_base *obj = _import->obj();
00587                         if (!obj)
00588                         {
00589                                 error(efmt_bad_import, _importstreampath.string(), true);
00590                                 _abort = true;
00591                                 return;
00592                         }
00593         
00594                         // make sure that the object get's the correct name.
00595                         if (!_import->_rootname.empty())
00596                                 set_persistable_obj_name(obj, _import->_rootname);
00597                         
00598                         // set the filepath for the object.
00599                         if (!test_persistable(obj))
00600                                 return;
00601                         if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00602                                 obj_out("setting the path to [" + _importstreampath.string() + "]", obj);
00603                         // TBD: the following should be a real path.
00604                         obj->persistable()->set_file_path(_importstreampath.string());
00605                                 
00606                         // if the object on the top of the stack is a composite, then add this object to that.
00607                         if (!test_composition(_stack.top()))
00608                                 return;
00609                         
00610                         if (_debug & PARSEOBJ_OBJECT_DEBUG)
00611                         {
00612                                 obj_out("composite (4)", _stack.top());
00613                                 obj_out("adding to composite (4)", obj);
00614                         }
00615         
00616                         if (!_stack.top()->composition()->composite()->add(obj, true))
00617                         {
00618                                 error(efmt_bad_aggr_obj, obj_name(_stack.top()), obj_type(_stack.top()), true);
00619                                 _abort = true;
00620                                 return;
00621                         }
00622                         
00623                         // finish up.
00624                         delete _importstream;
00625                         _importstream = 0;
00626                         delete _import;
00627                         _import = 0;
00628                 }
00629         }
00630         else if (element == "param")
00631         {
00632                 // end of parameters.
00633         }
00634         else if (element == "pathparam")
00635         {
00636                 // end of path parameters.
00637         }
00638         else if (element == "var")
00639         {
00640                 // end of variable.
00641         }
00642         else if (element == "pathvar")
00643         {
00644                 // end of path variable.
00645         }
00646         else if (element == S("fragment"))
00647         {
00648                 if (!_fragment)
00649                 {
00650                         error(efmt_bad_endfragment, element, true);
00651                         _abort = true;
00652                         return;
00653                 }
00654 
00655                 // pop the stack.
00656                 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00657                         obj_out("popping", _stack.top());
00658                 _stack.pop();
00659                 _fragment = false;
00660         }
00661         else if (_inmember)
00662         {
00663                 if (_debug & PARSEOBJ_OBJECT_DEBUG)
00664                         obj_out("setting member [" + element + "] of ", _stack.top());
00665                 if (!test_persistable(_stack.top()))
00666                         return;
00667 
00668                 try {
00669                         _stack.top()->persistable()->set(element, dosubs(_cdata));
00670                 }
00671                 catch (...)
00672                 {
00673                         error(efmt_bad_data, element, _cdata, true);
00674                         _abort = true;
00675                         return;
00676                 }
00677                 if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00678                         *_errorhandler << ">> emptying cdata." << std::endl;
00679                 _cdata = "";
00680                 _inmember = false;
00681         }
00682         else
00683         {
00684                 ph::common::object_base *obj = _stack.top();
00685                 if (obj_type(obj) == element)
00686                 {
00687                         // see if _cdata is anything other than whitespace.
00688                         if (_cdata.find_first_not_of(" \t\n") != std::string::npos)
00689                                 *_errorhandler << ">> Ignoring cdata. [" << _cdata << "]" << std::endl;
00690                         
00691                         // closing out the current object
00692                         if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00693                                 obj_out("closing out ", obj);
00694                         _stack.pop();
00695                         
00696                         if (_stack.empty())
00697                         { 
00698                                 // we are at the end. Pickup the object.
00699                                 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00700                                         obj_out("stashing as _obj", obj);
00701                                 _obj = obj;
00702                         }
00703                 }
00704                 else if (obj->composition() && obj->composition()->composite())
00705                 {
00706                         if (obj_name(obj) != element)
00707                         {
00708                                 error(efmt_bad_tag, element, true);
00709                                 _abort = true;
00710                                 return;
00711                         }
00712 
00713                         if (_cdata.find_first_not_of(" \t\n") != std::string::npos)
00714                         {
00715                                 if (obj->composition()->composite()->singleton()
00716                                         && obj->composition()->composite()->count() == 0)
00717                                 {
00718                                         // find the object that the cdata refers to, and add that as a reference.
00719                                         ph::common::object_base *o = _context->find_object(_root, obj, _cdata);
00720                                         if (!o)
00721                                         {
00722                                                 error(efmt_bad_ref_location, _cdata, true);
00723                                                 _abort = true;
00724                                                 return;
00725                                         }
00726         
00727                                         if (_debug & PARSEOBJ_OBJECT_DEBUG)
00728                                         {
00729                                                 obj_out("composite (5)", obj);
00730                                                 obj_out("adding to composite (5)", o);
00731                                         }
00732                                         
00733                                         // add the object to the composite, but it's not owned.
00734                                         if (!obj->composition()->composite()->add(o, false))
00735                                         {
00736                                                 error(efmt_bad_aggr_obj, obj_name(obj), obj_type(obj), true);
00737                                                 _abort = true;
00738                                                 return;
00739                                         }
00740                                         
00741                                         // we have used up cdata.
00742                                         if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00743                                                 *_errorhandler << ">> emptying cdata." << std::endl;
00744                                         _cdata = "";
00745                                         
00746                                         if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00747                                                 obj_out("popping because composite", obj);
00748                                         // if we are closing off a composite, just pop it off.
00749                                         _stack.pop();
00750                                 }
00751                                 else
00752                                 {
00753                                         // we can help generic out at this point. The only way that it knows if
00754                                         // an element is a composite or if it's a member is if it get's some
00755                                         // type of non-whitespace CDATA through. We call set as if this
00756                                         // is a member. This might cause some concern for other parsers since they'll
00757                                         // see members AND composites, but they can be written the same.
00758                                         
00759                                         if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00760                                                 obj_out("popping because composite (possible member?)", obj);
00761                                         // if we are closing off a composite, just pop it off.
00762                                         _stack.pop();
00763                                         if (_debug & PARSEOBJ_OBJECT_DEBUG)
00764                                                 obj_out("setting member [" + element + "] of ", _stack.top());
00765                                         if (!test_persistable(_stack.top()))
00766                                                 return;
00767                                         try 
00768                                         {
00769                                                 _stack.top()->persistable()->set(element, dosubs(_cdata));
00770                                         }
00771                                         catch (...)
00772                                         {
00773                                                 error(efmt_bad_data, element, _cdata, true);
00774                                                 _abort = true;
00775                                                 return;
00776                                         }
00777                                         if (_debug & (PARSEOBJ_DATA_DEBUG | PARSEOBJ_ELEMENT_DEBUG | PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00778                                                 *_errorhandler << ">> emptying cdata." << std::endl;
00779                                         _cdata = "";
00780                                 }
00781                         }                       
00782                         else
00783                         {
00784                                 if (_debug & (PARSEOBJ_STACK_DEBUG | PARSEOBJ_OBJECT_DEBUG))
00785                                         obj_out("popping because composite", obj);
00786                                 // if we are closing off a composite, just pop it off.
00787                                 _stack.pop();
00788                         }
00789                 }
00790                 else
00791                 {
00792                         error(efmt_bad_tag, element, true);
00793                         _abort = true;
00794                 }
00795         }
00796 }
00797 
00798 ph::common::object_base *xml::parseobj::create_object(ph::common::object_base *top, const xmlstring &type, const xmlstring &name)
00799 {
00800         // see if the tos is a composite.
00801         // if it's a composite, then we need to use it's parent
00802         // to clone actual objects off.
00803         ph::common::object_base *topsearch = top;
00804         if (top->composition() && top->composition()->composite())
00805         {
00806                 if (!test_outerable(top))
00807                         return 0;
00808                 topsearch = top->outerable()->outer();
00809         }
00810         
00811         if (_debug & PARSEOBJ_OBJECT_DEBUG)
00812                 *_errorhandler << ">> create_object type: [" << type 
00813                         << "] name: [" << name 
00814                         << "] top->name: [" << obj_name(top) << "]" << std::endl;
00815 
00816         // try to create the new object using the top object.
00817         if (!test_persistable(top))
00818                 return 0;
00819         ph::common::object_base *obj = topsearch->persistable()->create(type, name, _context);
00820         
00821         // if the above fails, ask the object manager.
00822         if (!obj)
00823                 obj = _context->create(type, name);
00824         
00825         if (obj)
00826         {       
00827                 // make sure we set the parent straight away so that templates etc
00828                 // are used correctly.
00829                 if (obj->outerable())
00830                         obj->outerable()->outer(top);
00831         }
00832         
00833         return obj;
00834 }
00835 
00836 xmlstring xml::parseobj::dosubs(const xmlstring &s)
00837 /*
00838         Substitute string values in the string.
00839 
00840         All $(arg) pairs are substitute.
00841 */
00842 {
00843         xmlstring remain = s;
00844         xmlstring news = S("");
00845         int loc = remain.find(S("$("));
00846         if (loc >= 0)
00847         {
00848                 while (loc >= 0)
00849                 {
00850                         news += remain.substr(0, loc);
00851                         remain = remain.substr(loc);
00852                         loc = remain.find(L')');
00853                         if (loc >= 0)
00854                         {
00855                                 xmlstring token = remain.substr(2, loc-2);
00856                                 remain = remain.substr(loc+1);
00857 
00858                                 std::string value;
00859                                 if (find_param(token, &value))
00860                                         news += value;
00861                                 else
00862                                         news += S("$(") + token + S(")");
00863                         }
00864                         loc = remain.find(S("$("));
00865                 }
00866                 news += remain;
00867         }
00868 
00869         if (news.empty())
00870                 return s;
00871         else
00872                 return news;
00873 }
00874 
00875 void xml::parseobj::add_params(const std::map<std::string, std::string> &params)
00876 {
00877         // copy the map. This code could be much simpler I think...
00878         std::for_each(params.begin(), params.end(), 
00879                 boost::bind(&add_param_1,
00880                         &_params,
00881                         boost::bind(&std::map<std::string, std::string>::value_type::first, _1),
00882                         boost::bind(&std::map<std::string, std::string>::value_type::second, _1)));
00883 }
00884 
00885 void xml::parseobj::add_param_1(std::map<std::string, std::string> *params, 
00886                 const std::string &name, const std::string &val)
00887 {
00888         (*params)[name] = val;
00889 }
00890 
00891 void xml::parseobj::add_param(const std::string &name, const std::string &val) 
00892 {
00893         add_param_1(&_params, name, val);
00894 }
00895 
00896 bool xml::parseobj::find_param(const std::string &name, std::string *value)
00897 {
00898         std::map<std::string, std::string>::iterator i = _params.find(name);
00899         if (i == _params.end())
00900                 return false;
00901         *value = i->second;
00902         return true;
00903 }
00904 
00905 bool xml::parseobj::test_outerable(ph::common::object_base *obj)
00906 {
00907         if (!obj->outerable())
00908         {
00909                 error(efmt_no_interface, "outerable", obj_name(obj), true);
00910                 _abort = true;
00911                 return false;
00912         }
00913         return true;
00914 }
00915 
00916 bool xml::parseobj::test_persistable(ph::common::object_base *obj)
00917 {
00918         if (!obj->persistable())
00919         {
00920                 error(efmt_no_interface, "persistable", obj_name(obj), true);
00921                 _abort = true;
00922                 return false;
00923         }
00924         return true;
00925 }
00926 
00927 bool xml::parseobj::test_composition(ph::common::object_base *obj)
00928 {
00929         if (!obj->composition())
00930         {
00931                 error(efmt_no_interface, "composition", obj_name(obj), true);
00932                 _abort = true;
00933                 return false;
00934         }
00935         if (!obj->composition()->composite())
00936         {
00937                 error(efmt_not_composite, obj_name(obj), true);
00938                 _abort = true;
00939                 return false;
00940         }
00941         return true;
00942 }
00943 
00944 void xml::parseobj::obj_out(const std::string &msg, ph::common::object_base *obj)
00945 {
00946         *_errorhandler << ">> " << msg << " [" << obj_type(obj) << ", " << obj_name(obj) << "]" << std::endl;
00947 }
00948 
00949 void xml::parseobj::element_out(const std::string &msg, const xmlstring &element)
00950 {
00951         *_errorhandler << ">> " << msg << " [" << element << "]" << std::endl;
00952 }
00953 
00954 // TBD: These don't work.
00955 xmlstring xml::parseobj::getdecodedattrval(const std::vector<xmlstring> &atts, int index)
00956 {
00957         xmlstring val = attrval(atts, index);
00958         xmlstring news;
00959         if (decodexmldata(val, &news))
00960                 return news;
00961         else
00962                 return val;
00963 }
00964 
00965 xmlstring xml::parseobj::getdecodedexpectedattr(const std::vector<xmlstring> &atts, const xmlstring &token)
00966 {
00967         xmlstring val = expectedattr(atts, token);
00968         xmlstring news;
00969         if (decodexmldata(val, &news))
00970                 return news;
00971         else
00972                 return val;
00973 }
00974 
00975 xmlstring xml::parseobj::getdecodedattr(const std::vector<xmlstring> &atts, const xmlstring &token)
00976 {
00977         xmlstring val = attr(atts, token);
00978         xmlstring news;
00979         if (decodexmldata(val, &news))
00980                 return news;
00981         else
00982                 return val;
00983 }

Generated on Wed Apr 5 22:03:26 2006 for cppxmlobj by  doxygen 1.4.3